/** * @interface_method_impl{VBOXSERVICE,pfnWorker} */ DECLCALLBACK(int) vgsvcTimeSyncWorker(bool volatile *pfShutdown) { RTTIME Time; char sz[64]; int rc = VINF_SUCCESS; /* * Tell the control thread that it can continue spawning services. */ RTThreadUserSignal(RTThreadSelf()); /* * The Work Loop. */ for (;;) { /* * Try get a reliable time reading. */ int cTries = 3; do { /* query it. */ RTTIMESPEC GuestNow0, GuestNow, HostNow; RTTimeNow(&GuestNow0); int rc2 = VbglR3GetHostTime(&HostNow); if (RT_FAILURE(rc2)) { if (g_cTimeSyncErrors++ < 10) VGSvcError("vgsvcTimeSyncWorker: VbglR3GetHostTime failed; rc2=%Rrc\n", rc2); break; } RTTimeNow(&GuestNow); /* calc latency and check if it's ok. */ RTTIMESPEC GuestElapsed = GuestNow; RTTimeSpecSub(&GuestElapsed, &GuestNow0); if ((uint32_t)RTTimeSpecGetMilli(&GuestElapsed) < g_TimeSyncMaxLatency) { /* * Set the time once after we were restored. * (Of course only if the drift is bigger than MinAdjust) */ uint32_t TimeSyncSetThreshold = g_TimeSyncSetThreshold; if (g_fTimeSyncSetOnRestore) { uint64_t idNewSession = g_idTimeSyncSession; VbglR3GetSessionId(&idNewSession); if (idNewSession != g_idTimeSyncSession) { VGSvcVerbose(3, "vgsvcTimeSyncWorker: The VM session ID changed, forcing resync.\n"); TimeSyncSetThreshold = 0; g_idTimeSyncSession = idNewSession; } } /* * Calculate the adjustment threshold and the current drift. */ uint32_t MinAdjust = RTTimeSpecGetMilli(&GuestElapsed) * g_TimeSyncLatencyFactor; if (MinAdjust < g_TimeSyncMinAdjust) MinAdjust = g_TimeSyncMinAdjust; RTTIMESPEC Drift = HostNow; RTTimeSpecSub(&Drift, &GuestNow); if (RTTimeSpecGetMilli(&Drift) < 0) MinAdjust += g_TimeSyncMinAdjust; /* extra buffer against moving time backwards. */ RTTIMESPEC AbsDrift = Drift; RTTimeSpecAbsolute(&AbsDrift); if (g_cVerbosity >= 3) { VGSvcVerbose(3, "vgsvcTimeSyncWorker: Host: %s (MinAdjust: %RU32 ms)\n", RTTimeToString(RTTimeExplode(&Time, &HostNow), sz, sizeof(sz)), MinAdjust); VGSvcVerbose(3, "vgsvcTimeSyncWorker: Guest: - %s => %RDtimespec drift\n", RTTimeToString(RTTimeExplode(&Time, &GuestNow), sz, sizeof(sz)), &Drift); } uint32_t AbsDriftMilli = RTTimeSpecGetMilli(&AbsDrift); if (AbsDriftMilli > MinAdjust) { /* * Ok, the drift is above the threshold. * * Try a gradual adjustment first, if that fails or the drift is * too big, fall back on just setting the time. */ if ( AbsDriftMilli > TimeSyncSetThreshold || g_fTimeSyncSetNext || !vgsvcTimeSyncAdjust(&Drift)) { vgsvcTimeSyncCancelAdjust(); vgsvcTimeSyncSet(&Drift); } } else vgsvcTimeSyncCancelAdjust(); break; } VGSvcVerbose(3, "vgsvcTimeSyncWorker: %RDtimespec: latency too high (%RDtimespec) sleeping 1s\n", GuestElapsed); RTThreadSleep(1000); } while (--cTries > 0); /* Clear the set-next/set-start flag. */ g_fTimeSyncSetNext = false; /* * Block for a while. * * The event semaphore takes care of ignoring interruptions and it * allows us to implement service wakeup later. */ if (*pfShutdown) break; int rc2 = RTSemEventMultiWait(g_TimeSyncEvent, g_TimeSyncInterval); if (*pfShutdown) break; if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2)) { VGSvcError("vgsvcTimeSyncWorker: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2); rc = rc2; break; } } vgsvcTimeSyncCancelAdjust(); RTSemEventMultiDestroy(g_TimeSyncEvent); g_TimeSyncEvent = NIL_RTSEMEVENTMULTI; return rc; }
/** * Process the testcases found in the filter. * * @param pszFilter The filter (winnt) to pass to RTDirOpenFiltered for * selecting the testcases. * @param pszDir The directory we're processing. */ static void Process(const char *pszFilter, const char *pszDir) { /* * Open and enumerate the directory. */ PRTDIR pDir; int rc = RTDirOpenFiltered(&pDir, pszFilter, RTDIRFILTER_WINNT, 0); if (RT_SUCCESS(rc)) { for (;;) { RTDIRENTRY DirEntry; rc = RTDirRead(pDir, &DirEntry, NULL); if (RT_FAILURE(rc)) { if (rc == VERR_NO_MORE_FILES) rc = VINF_SUCCESS; else RTPrintf("tstRunTestcases: reading '%s' -> %Rrc\n", pszFilter, rc); break; } /* * Construct the testcase name. */ char *pszTestcase; RTStrAPrintf(&pszTestcase, "%s/%s", pszDir, DirEntry.szName); if (!pszTestcase) { RTPrintf("tstRunTestcases: out of memory!\n"); rc = VERR_NO_MEMORY; break; } if (IsTestcaseIncluded(pszTestcase)) { /* * Execute the testcase. */ RTPrintf("*** %s: Executing...\n", pszTestcase); RTStrmFlush(g_pStdOut); const char *papszArgs[2]; papszArgs[0] = pszTestcase; papszArgs[1] = NULL; RTPROCESS Process; rc = RTProcCreate(pszTestcase, papszArgs, RTENV_DEFAULT, 0, &Process); if (RT_SUCCESS(rc)) { /* * Wait for the process and collect it's return code. * If it takes too long, we'll terminate it and continue. */ RTTIMESPEC Start; RTTimeNow(&Start); RTPROCSTATUS ProcStatus; for (;;) { rc = RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus); if (rc != VERR_PROCESS_RUNNING) break; RTTIMESPEC Now; if (RTTimeSpecGetMilli(RTTimeSpecSub(RTTimeNow(&Now), &Start)) > 120*1000 /* 1 min */) { RTPrintf("*** %s: FAILED - timed out. killing it.\n", pszTestcase); RTProcTerminate(Process); RTThreadSleep(100); RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus); g_cFailures++; break; } RTThreadSleep(100); } /* * Examin the exit status. */ if (RT_SUCCESS(rc)) { if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL && ProcStatus.iStatus == 0) { RTPrintf("*** %s: PASSED\n", pszTestcase); g_cPasses++; } else { RTPrintf("*** %s: FAILED\n", pszTestcase); g_cFailures++; } } else if (rc != VERR_PROCESS_RUNNING) { RTPrintf("tstRunTestcases: %s: RTProcWait failed -> %Rrc\n", pszTestcase, rc); g_cFailures++; } } else { RTPrintf("tstRunTestcases: %s: failed to start -> %Rrc\n", pszTestcase, rc); g_cFailures++; } } else { RTPrintf("tstRunTestcases: %s: SKIPPED\n", pszTestcase); g_cSkipped++; } RTStrFree(pszTestcase); } /* enumeration loop */ RTDirClose(pDir); } else RTPrintf("tstRunTestcases: opening '%s' -> %Rrc\n", pszDir, rc); }