VirtualBox

Changeset 1692 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Mar 26, 2007 7:22:22 AM (18 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
19840
Message:

Testcase for reading the TSC on all CPUs in an SMP system.

Location:
trunk/src/VBox/Runtime/testcase
Files:
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/testcase/Makefile

    r1190 r1692  
    3232        tstLdrLoad \
    3333        tstAvl \
     34        tstTSC \
    3435        tstTimer \
    3536        tstTime \
     
    8283tstTimer_SOURCES = tstTimer.cpp
    8384
     85tstTSC_SOURCES = tstTSC.cpp
     86
    8487tstTime_SOURCES = tstTime.cpp
    8588
  • trunk/src/VBox/Runtime/testcase/tstTSC.cpp

    r1688 r1692  
    11/* $Id$ */
    22/** @file
    3  * InnoTek Portable Runtime Testcase - Simple RTTime test.
     3 * InnoTek Portable Runtime Testcase - SMP TSC testcase.
    44 */
    55
     
    2323*   Header Files                                                               *
    2424*******************************************************************************/
    25 #ifdef __WIN__
    26 # include <Windows.h>
    27 
    28 #elif defined __L4__
    29 
    30 #else /* posix */
    31 # include <sys/time.h>
    32 #endif
    33 
    34 #include <iprt/time.h>
    35 #include <iprt/stream.h>
    3625#include <iprt/runtime.h>
    37 #include <iprt/thread.h>
    38 #include <VBox/sup.h>
    39 
    40 DECLINLINE(uint64_t) OSNanoTS(void)
     26
     27/*******************************************************************************
     28*   Structures and Typedefs                                                    *
     29*******************************************************************************/
     30typedef struct TSCDATA
    4131{
    42 #ifdef __WIN__
    43     uint64_t u64; /* manual say larger integer, should be safe to assume it's the same. */
    44     GetSystemTimeAsFileTime((LPFILETIME)&u64);
    45     return u64 * 100;
    46 
    47 #elif defined __L4__
    48     /** @todo fix a different timesource on l4. */
    49     return RTTimeNanoTS();
    50 
    51 #else /* posix */
    52 
    53     struct timeval tv;
    54     gettimeofday(&tv, NULL);
    55     return (uint64_t)tv.tv_sec  * (uint64_t)(1000 * 1000 * 1000)
    56          + (uint64_t)(tv.tv_usec * 1000);
    57 #endif
     32    /** The TSC.  */
     33    uint64_t volatile   TSC;           
     34    /** The APIC ID. */
     35    uint8_t volatile    u8ApicId;
     36    /** Did it succeed? */
     37    bool volatile       fRead;
     38    /** Did it fail? */
     39    bool volatile       fFailed;
     40    /** The thread handle. */
     41    RTTHREAD            Thread;
     42} TSCDATA, *PTSCDATA;
     43
     44/*******************************************************************************
     45*   Global Variables                                                           *
     46*******************************************************************************/
     47/** The number of CPUs waiting on their user event semaphore. */
     48static volatile uint32_t g_cWaiting;
     49/** The number of CPUs ready (in spin) to do the TSC read. */
     50static volatile uint32_t g_cReady;
     51/** The variable the CPUs are spinning on.
     52 * 0: Spin.
     53 * 1: Go ahead.
     54 * 2: You're too late, back to square one. */
     55static volatile uint32_t g_u32Go;
     56/** The number of CPUs that managed to read the TSC. */
     57static volatile uint32_t g_cRead;
     58/** The number of CPUs that failed to read the TSC. */
     59static volatile uint32_t g_cFailed;
     60
     61/** Indicator forcing the threads to quit. */
     62static volatile bool g_fDone;
     63
     64
     65/*******************************************************************************
     66*   Internal Functions                                                         *
     67*******************************************************************************/
     68static DECLCALLBACK(int) ThreadFunction(RTTHREAD Thread, void *pvUser);
     69
     70
     71/**
     72 * Thread function for catching the other cpus.
     73 * 
     74 * @returns VINF_SUCCESS (we don't care).
     75 * @param   Thread  The thread handle.
     76 * @param   pvUser  PTSCDATA.
     77 */
     78static DECLCALLBACK(int) ThreadFunction(RTTHREAD Thread, void *pvUser)
     79{
     80    PTSCDATA pTscData = (PTSCDATA)pvUser;
     81
     82    while (!g_fDone)
     83    {
     84        /*
     85         * Wait.
     86         */
     87        ASMAtomicIncU32(&g_cWaiting);
     88        RTThreadUserWait(Thread, RT_INDEFINITE_WAIT);
     89        RTThreadUserReset(Thread);
     90        ASMAtomicDecU32(&g_cWaiting);
     91        if (g_fDone)
     92            break;
     93
     94        /*
     95         * Spin.
     96         */
     97        ASMAtomicIncU32(&g_cReady);
     98        while (!g_fDone)
     99        {
     100            const uint8_t   ApicId1 = ASMGetApicId();
     101            const uint64_t  TSC1    = ASMReadTSC();
     102            const uint32_t  u32Go   = g_u32Go;
     103            if (u32Go == 0)
     104                continue;
     105
     106            if (u32Go == 1)
     107            {
     108                /* do the reading. */
     109                const uint8_t   ApicId2 = ASMGetApicId();
     110                const uint64_t  TSC2    = ASMReadTSC();
     111                const uint8_t   ApicId3 = ASMGetApicId();
     112                const uint64_t  TSC3    = ASMReadTSC();
     113                const uint8_t   ApicId4 = ASMGetApicId();
     114
     115                if (    ApicId1 == ApicId2
     116                    &&  ApicId1 == ApicId3
     117                    &&  ApicId1 == ApicId4
     118                    &&  TSC3 - TSC1 < 1750 /* WARNING: This is just a guess, increase if it doesn't work for you. */
     119                    &&  TSC2 - TSC1 < TSC3 - TSC1
     120                    )
     121                {
     122                    /* succeeded. */
     123                    pTscData->TSC = TSC2;
     124                    pTscData->u8ApicId = ApicId1;
     125                    pTscData->fFailed = false;
     126                    pTscData->fRead = true;
     127                    ASMAtomicIncU32(&g_cRead);
     128                    break;
     129                }
     130            }
     131
     132            /* failed */
     133            pTscData->fFailed = true;
     134            pTscData->fRead = false;
     135            ASMAtomicIncU32(&g_cFailed);
     136            break;
     137        }
     138    }
     139   
     140    return VINF_SUCCESS;
    58141}
    59 
    60142
    61143
    62144int main()
    63145{
    64     unsigned cErrors = 0;
    65     int i;
    66146    RTR3Init();
    67 SUPInit();
    68     RTPrintf("tstTime-2: TESTING...\n");
    69147
    70148    /*
    71      * RTNanoTimeTS() shall never return something which
    72      * is less or equal to the return of the previous call.
     149     * This is only relevant to on SMP systems.
    73150     */
    74 
    75     OSNanoTS(); RTTimeNanoTS(); RTThreadYield();
    76     uint64_t u64RTStartTS = RTTimeNanoTS();
    77     uint64_t u64OSStartTS = OSNanoTS();
    78 #define NUMBER_OF_CALLS (100*_1M)
    79 
    80     for (i = 0; i < NUMBER_OF_CALLS; i++)
    81         RTTimeNanoTS();
    82 
    83     uint64_t u64RTElapsedTS = RTTimeNanoTS();
    84     uint64_t u64OSElapsedTS = OSNanoTS();
    85     u64RTElapsedTS -= u64RTStartTS;
    86     u64OSElapsedTS -= u64OSStartTS;
    87     int64_t i64Diff = u64OSElapsedTS >= u64RTElapsedTS ? u64OSElapsedTS - u64RTElapsedTS : u64RTElapsedTS - u64OSElapsedTS;
    88     if (i64Diff > (int64_t)(u64OSElapsedTS / 1000))
    89     {
    90         RTPrintf("tstTime-2: error: total time differs too much! u64OSElapsedTS=%#llx u64RTElapsedTS=%#llx delta=%lld\n",
    91                  u64OSElapsedTS, u64RTElapsedTS, u64OSElapsedTS - u64RTElapsedTS);
    92         cErrors++;
    93     }
    94     else
    95         RTPrintf("tstTime-2: total time difference: u64OSElapsedTS=%#llx u64RTElapsedTS=%#llx delta=%lld\n",
    96                  u64OSElapsedTS, u64RTElapsedTS, u64OSElapsedTS - u64RTElapsedTS);
    97 
    98 #if defined __WIN__ || defined __LINUX__
    99     RTPrintf("tstTime-2: RTTime1nsSteps -> %u out of %u calls\n", RTTime1nsSteps(), NUMBER_OF_CALLS);
    100 #endif
    101     if (!cErrors)
    102         RTPrintf("tstTime-2: SUCCESS\n");
    103     else
    104         RTPrintf("tstTime-2: FAILURE - %d errors\n", cErrors);
    105     return !!cErrors;
     151    const unsigned cCpus = RTSystemProcessorGetCount();
     152    if (cCpus <= 1)
     153    {
     154        RTPrintf("tstTSC: SKIPPED - Only relevant on SMP systems\n");
     155        return 0;
     156    }
     157
     158    /*
     159     * Create the threads.
     160     */
     161    static TSCDATA s_aData[254];
     162    uint32_t i;
     163    if (cCpus > RT_ELEMENTS(s_aData))
     164    {
     165        RTPrintf("tstTSC: FAILED - too many CPUs (%u)\n", cCpus);
     166        return 1;
     167    }
     168
     169    /* ourselves. */
     170    s_aData[0].Thread = RTThreadSelf();
     171
     172    /* the others */
     173    for (i = 1; i < cCpus; i++)
     174    {
     175        int rc = RTThreadCreate(&s_aData[i].Thread, ThreadFunction, &s_aData[i], 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "OTHERCPU");
     176        if (RT_FAILURE(rc))
     177        {
     178            RTPrintf("tstTSC: FAILURE - RTThreatCreate failed when creating thread #%u, rc=%Rrc!\n", i, rc);
     179            ASMAtomicXchgSize(&g_fDone, true);
     180            while (i-- > 1)
     181            {
     182                RTThreadUserSignal(s_aData[i].Thread);
     183                RTThreadWait(s_aData[i].Thread, 5000, NULL);
     184            }
     185            return 1;
     186        }
     187    }
     188
     189    /*
     190     * Retry untill we get lucky (or give up).
     191     */
     192    for (unsigned cTries = 0; ; cTries++)
     193    {
     194        if (cTries > 1024)
     195        {
     196            RTPrintf("tstTSC: FAILURE - %d attempts, giving.\n", cTries);
     197            break;
     198        }
     199
     200        /*
     201         * Wait for the other threads to get ready (brute force active wait, I'm lazy).
     202         */
     203        i = 0;
     204        while (g_cWaiting < cCpus - 1)
     205        {
     206            if (i++ > _2G32)
     207                break;
     208            RTThreadSleep(i & 0xf);
     209        }
     210        if (g_cWaiting != cCpus - 1)
     211        {
     212            RTPrintf("tstTSC: FAILURE - threads failed to get waiting (%d != %d (i=%d))\n", g_cWaiting + 1, cCpus, i);
     213            break;
     214        }
     215
     216        /*
     217         * Send them spinning.
     218         */
     219        ASMAtomicXchgU32(&g_cReady, 0);
     220        ASMAtomicXchgU32(&g_u32Go, 0);
     221        ASMAtomicXchgU32(&g_cRead, 0);
     222        ASMAtomicXchgU32(&g_cFailed, 0);
     223        for (i = 1; i < cCpus; i++)
     224        {
     225            ASMAtomicXchgSize(&s_aData[i].fFailed, false);
     226            ASMAtomicXchgSize(&s_aData[i].fRead, false);
     227            ASMAtomicXchgU8(&s_aData[i].u8ApicId, 0xff);
     228
     229            int rc = RTThreadUserSignal(s_aData[i].Thread);
     230            if (RT_FAILURE(rc))
     231                RTPrintf("tstTSC: WARNING - RTThreadUserSignal(%#u) -> rc=%Rrc!\n", i, rc);
     232        }
     233
     234        /* wait for them to get ready. */
     235        i = 0;
     236        while (g_cReady < cCpus - 1)
     237        {
     238            if (i++ > _2G32)
     239                break;
     240        }
     241        if (g_cReady != cCpus - 1)
     242        {
     243            RTPrintf("tstTSC: FAILURE - threads failed to get ready (%d != %d, i=%d)\n", g_cWaiting + 1, cCpus, i);
     244            break;
     245        }
     246
     247        /*
     248         * Flip the "go" switch and do our readings.
     249         * We give the other threads the slack it takes to two extra TSC and APIC ID reads.
     250         */
     251        const uint8_t   ApicId1 = ASMGetApicId();
     252        const uint64_t  TSC1    = ASMReadTSC();
     253        ASMAtomicXchgU32(&g_u32Go, 1);
     254        const uint8_t   ApicId2 = ASMGetApicId();
     255        const uint64_t  TSC2    = ASMReadTSC();
     256        const uint8_t   ApicId3 = ASMGetApicId();
     257        const uint64_t  TSC3    = ASMReadTSC();
     258        const uint8_t   ApicId4 = ASMGetApicId();
     259        const uint64_t  TSC4    = ASMReadTSC();
     260        ASMAtomicXchgU32(&g_u32Go, 2);
     261        const uint8_t   ApicId5 = ASMGetApicId();
     262        const uint64_t  TSC5    = ASMReadTSC();
     263        const uint8_t   ApicId6 = ASMGetApicId();
     264
     265        /* Compose our own result. */
     266        if (    ApicId1 == ApicId2
     267            &&  ApicId1 == ApicId3
     268            &&  ApicId1 == ApicId4
     269            &&  ApicId1 == ApicId5
     270            &&  ApicId1 == ApicId6
     271            &&  TSC5 - TSC1 < 2000  /* WARNING: This is just a guess, increase if it doesn't work for you. */
     272            &&  TSC4 - TSC1 < TSC5 - TSC1
     273            &&  TSC3 - TSC1 < TSC4 - TSC1
     274            &&  TSC2 - TSC1 < TSC3 - TSC1
     275            )
     276        {
     277            /* succeeded. */
     278            s_aData[0].TSC = TSC2;
     279            s_aData[0].u8ApicId = ApicId1;
     280            s_aData[0].fFailed = false;
     281            s_aData[0].fRead = true;
     282            ASMAtomicIncU32(&g_cRead);
     283        }
     284        else
     285        {
     286            /* failed */
     287            s_aData[0].fFailed = true;
     288            s_aData[0].fRead = false;
     289            ASMAtomicIncU32(&g_cFailed);
     290        }
     291
     292        /*
     293         * Wait a little while to let the other ones to finish.
     294         */
     295        i = 0;
     296        while (g_cRead + g_cFailed < cCpus)
     297        {
     298            if (i++ > _2G32)
     299                break;
     300            if (i > _1M)
     301                RTThreadSleep(i & 0xf);
     302        }
     303        if (g_cRead + g_cFailed != cCpus)
     304        {
     305            RTPrintf("tstTSC: FAILURE - threads failed to complete reading (%d + %d != %d)\n", g_cRead, g_cFailed, cCpus);
     306            break;
     307        }
     308
     309        /*
     310         * If everone succeeded, print the results.
     311         */
     312        if (!g_cFailed)
     313        {
     314            RTPrintf(" #  ID  TSC\n"
     315                     "-----------------------\n");
     316            for (i = 0; i < cCpus; i++)
     317                RTPrintf("%2d  %02x %RX64\n", i, s_aData[i].u8ApicId, s_aData[i].TSC);
     318            RTPrintf("(Needed %u attempt%s.)\n", cTries + 1, cTries ? "s" : "");
     319            break;
     320        }
     321    }
     322
     323    /*
     324     * Destroy the threads.
     325     */
     326    ASMAtomicXchgSize(&g_fDone, true);
     327    for (i = 1; i < cCpus; i++)
     328    {
     329        int rc = RTThreadUserSignal(s_aData[i].Thread);
     330        if (RT_FAILURE(rc))
     331            RTPrintf("tstTSC: WARNING - RTThreadUserSignal(%#u) -> rc=%Rrc! (2)\n", i, rc);
     332    }
     333    for (i = 1; i < cCpus; i++)
     334    {
     335        int rc = RTThreadWait(s_aData[i].Thread, 5000, NULL);
     336        if (RT_FAILURE(rc))
     337            RTPrintf("tstTSC: WARNING - RTThreadWait(%#u) -> rc=%Rrc!\n", i, rc);
     338    }
     339
     340    return g_cFailed != 0 || g_cRead != cCpus;
    106341}
Note: See TracChangeset for help on using the changeset viewer.

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