/** * Callback function for consuming queued events. * * @param pvTarget queue? * @param rcIn ? * @param pvRefcon Pointer to the keyboard cache entry. * @param pvSender ? */ static void darwinQueueCallback(void *pvTarget, IOReturn rcIn, void *pvRefcon, void *pvSender) { struct KeyboardCacheData *pKeyboardEntry = (struct KeyboardCacheData *)pvRefcon; if (!pKeyboardEntry->ppHidQueueInterface) return; NOREF(pvTarget); NOREF(rcIn); NOREF(pvSender); /* * Consume the events. */ g_fOldHIDModifierMask = g_fHIDModifierMask; for (;;) { #ifdef DEBUG_PRINTF RTPrintf("dbg-ev: "); RTStrmFlush(g_pStdOut); #endif IOHIDEventStruct Event; AbsoluteTime ZeroTime = {0,0}; IOReturn rc = (*pKeyboardEntry->ppHidQueueInterface)->getNextEvent(pKeyboardEntry->ppHidQueueInterface, &Event, ZeroTime, 0); if (rc != kIOReturnSuccess) break; /* Translate the cookie value to a modifier mask. */ uint32_t fMask = 0; unsigned i = pKeyboardEntry->cCookies; while (i-- > 0) { if (pKeyboardEntry->aCookies[i].Cookie == Event.elementCookie) { fMask = pKeyboardEntry->aCookies[i].fMask; break; } } /* * Adjust the modifier mask. * * Note that we don't bother to deal with anyone pressing the same modifier * on 2 or more keyboard. That's not worth the effort involved. */ if (Event.value) g_fHIDModifierMask |= fMask; else g_fHIDModifierMask &= ~fMask; #ifdef DEBUG_PRINTF RTPrintf("t=%d c=%#x v=%#x cblv=%d lv=%p m=%#X\n", Event.type, Event.elementCookie, Event.value, Event.longValueSize, Event.value, fMask); RTStrmFlush(g_pStdOut); #endif } #ifdef DEBUG_PRINTF RTPrintf("dbg-ev: done\n"); RTStrmFlush(g_pStdOut); #endif }
static int tstRTCreateProcEx3Child(void) { int rc = RTR3InitExeNoArguments(0); if (RT_FAILURE(rc)) return RTMsgInitFailure(rc); RTStrmPrintf(g_pStdOut, "w"); RTStrmFlush(g_pStdOut); RTStrmPrintf(g_pStdErr, "o"); RTStrmFlush(g_pStdErr); RTStrmPrintf(g_pStdOut, "r"); RTStrmFlush(g_pStdOut); RTStrmPrintf(g_pStdErr, "k"); RTStrmFlush(g_pStdErr); RTStrmPrintf(g_pStdOut, "s"); return RTEXITCODE_SUCCESS; }
/** * Loads the modules. * * @returns RTEXITCODE_SUCCESS on success. */ static RTEXITCODE LoadModules(void) { for (uint32_t i = 0; i < RT_ELEMENTS(g_aModules); i++) { if (g_aModules[i].fPreload) { char szPath[RTPATH_MAX]; int rc = RTPathAppPrivateArch(szPath, sizeof(szPath)); if (RT_SUCCESS(rc)) rc = RTPathAppend(szPath, sizeof(szPath), g_aModules[i].pszName); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAppPrivateArch or RTPathAppend returned %Rrc", rc); void *pvImageBase; RTERRINFOSTATIC ErrInfo; RTErrInfoInitStatic(&ErrInfo); rc = SUPR3LoadModule(szPath, g_aModules[i].pszName, &g_aModules[i].pvImageBase, &ErrInfo.Core); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "SUPR3LoadModule failed for %s (%s): %s (rc=%Rrc)", g_aModules[i].pszName, szPath, ErrInfo.Core.pszMsg, rc); if (g_cVerbose >= 1) RTMsgInfo("Loaded '%s' ('%s') at %p\n", szPath, g_aModules[i].pszName, g_aModules[i].pvImageBase); } } RTStrmFlush(g_pStdOut); return RTEXITCODE_SUCCESS; }
/** * Waits a little while for a debuggger to attach. * * @returns True is a debugger have attached. * @param pVM Pointer to the VM. * @param enmEvent Event. */ bool dbgfR3WaitForAttach(PVM pVM, DBGFEVENTTYPE enmEvent) { /* * First a message. */ #ifndef RT_OS_L4 # if !defined(DEBUG) || defined(DEBUG_sandervl) || defined(DEBUG_frank) || defined(IEM_VERIFICATION_MODE) int cWait = 10; # else int cWait = HWACCMIsEnabled(pVM) && ( enmEvent == DBGFEVENT_ASSERTION_HYPER || enmEvent == DBGFEVENT_FATAL_ERROR) && !RTEnvExist("VBOX_DBGF_WAIT_FOR_ATTACH") ? 10 : 150; # endif RTStrmPrintf(g_pStdErr, "DBGF: No debugger attached, waiting %d second%s for one to attach (event=%d)\n", cWait / 10, cWait != 10 ? "s" : "", enmEvent); RTStrmFlush(g_pStdErr); while (cWait > 0) { RTThreadSleep(100); if (pVM->dbgf.s.fAttached) { RTStrmPrintf(g_pStdErr, "Attached!\n"); RTStrmFlush(g_pStdErr); return true; } /* next */ if (!(cWait % 10)) { RTStrmPrintf(g_pStdErr, "%d.", cWait / 10); RTStrmFlush(g_pStdErr); } cWait--; } #endif RTStrmPrintf(g_pStdErr, "Stopping the VM!\n"); RTStrmFlush(g_pStdErr); return false; }
static int tstHandleTableTest1(uint32_t uBase, uint32_t cMax, uint32_t cDelta, uint32_t cUnitsPerDot, bool fCallbacks, uint32_t fFlags) { const char *pszWithCtx = fFlags & RTHANDLETABLE_FLAGS_CONTEXT ? "WithCtx" : ""; uint32_t cRetainerCalls = 0; int rc; RTPrintf("tstHandleTable: TESTING RTHandleTableCreateEx(, 0"); if (fFlags & RTHANDLETABLE_FLAGS_LOCKED) RTPrintf(" | LOCKED"); if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT) RTPrintf(" | CONTEXT"); RTPrintf(", %#x, %#x,,)...\n", uBase, cMax); RTHANDLETABLE hHT; rc = RTHandleTableCreateEx(&hHT, fFlags, uBase, cMax, fCallbacks ? tstHandleTableTest1Retain : NULL, fCallbacks ? &cRetainerCalls : NULL); if (RT_FAILURE(rc)) { RTPrintf("\ntstHandleTable: FAILURE - RTHandleTableCreateEx failed, %Rrc!\n", rc); return 1; } /* fill it */ RTPrintf("tstHandleTable: TESTING RTHandleTableAlloc%s..", pszWithCtx); RTStrmFlush(g_pStdOut); uint32_t i = uBase; for (;; i++) { uint32_t h; if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT) rc = RTHandleTableAllocWithCtx(hHT, (void *)((uintptr_t)&i + (uintptr_t)i * 4), NULL, &h); else rc = RTHandleTableAlloc(hHT, (void *)((uintptr_t)&i + (uintptr_t)i * 4), &h); if (RT_SUCCESS(rc)) { if (h != i) { RTPrintf("\ntstHandleTable: FAILURE (%d) - h=%d, expected %d!\n", __LINE__, h, i); g_cErrors++; } } else if (rc == VERR_NO_MORE_HANDLES) { if (i < cMax) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, expected > 65534!\n", __LINE__, i); g_cErrors++; } break; } else { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, rc=%Rrc!\n", __LINE__, i, rc); g_cErrors++; } if (!(i % cUnitsPerDot)) { RTPrintf("."); RTStrmFlush(g_pStdOut); } } uint32_t const c = i; RTPrintf(" c=%#x\n", c); if (fCallbacks && cRetainerCalls != 0) { RTPrintf("tstHandleTable: FAILURE (%d) - cRetainerCalls=%#x expected 0!\n", __LINE__, cRetainerCalls); g_cErrors++; } /* look up all the entries */ RTPrintf("tstHandleTable: TESTING RTHandleTableLookup%s..", pszWithCtx); RTStrmFlush(g_pStdOut); cRetainerCalls = 0; for (i = uBase; i < c; i++) { void *pvExpect = (void *)((uintptr_t)&i + (uintptr_t)i * 4); void *pvObj; if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT) pvObj = RTHandleTableLookupWithCtx(hHT, i, NULL); else pvObj = RTHandleTableLookup(hHT, i); if (!pvObj) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableLookup%s failed!\n", __LINE__, i, pszWithCtx); g_cErrors++; } else if (pvObj != pvExpect) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, pvObj=%p expected %p\n", __LINE__, i, pvObj, pvExpect); g_cErrors++; } if (!(i % cUnitsPerDot)) { RTPrintf("."); RTStrmFlush(g_pStdOut); } } RTPrintf("\n"); if (fCallbacks && cRetainerCalls != c - uBase) { RTPrintf("tstHandleTable: FAILURE (%d) - cRetainerCalls=%#x expected %#x!\n", __LINE__, cRetainerCalls, c - uBase); g_cErrors++; } /* remove all the entries (in order) */ RTPrintf("tstHandleTable: TESTING RTHandleTableFree%s..", pszWithCtx); RTStrmFlush(g_pStdOut); cRetainerCalls = 0; for (i = uBase; i < c; i++) { void *pvExpect = (void *)((uintptr_t)&i + (uintptr_t)i * 4); void *pvObj; if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT) pvObj = RTHandleTableFreeWithCtx(hHT, i, NULL); else pvObj = RTHandleTableFree(hHT, i); if (!pvObj) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableLookup%s failed!\n", __LINE__, i, pszWithCtx); g_cErrors++; } else if (pvObj != pvExpect) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, pvObj=%p expected %p\n", __LINE__, i, pvObj, pvExpect); g_cErrors++; } else if ( fFlags & RTHANDLETABLE_FLAGS_CONTEXT ? RTHandleTableLookupWithCtx(hHT, i, NULL) : RTHandleTableLookup(hHT, i)) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableLookup%s succeeded after free!\n", __LINE__, i, pszWithCtx); g_cErrors++; } if (!(i % cUnitsPerDot)) { RTPrintf("."); RTStrmFlush(g_pStdOut); } } RTPrintf("\n"); if (fCallbacks && cRetainerCalls != c - uBase) { RTPrintf("tstHandleTable: FAILURE (%d) - cRetainerCalls=%#x expected %#x!\n", __LINE__, cRetainerCalls, c - uBase); g_cErrors++; } /* do a mix of alloc, lookup and free where there is a constant of cDelta handles in the table. */ RTPrintf("tstHandleTable: TESTING Alloc,Lookup,Free mix [cDelta=%#x]..", cDelta); RTStrmFlush(g_pStdOut); for (i = uBase; i < c * 2; i++) { /* alloc */ uint32_t hExpect = ((i - uBase) % (c - uBase)) + uBase; uint32_t h; if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT) rc = RTHandleTableAllocWithCtx(hHT, (void *)((uintptr_t)&i + (uintptr_t)hExpect * 4), NULL, &h); else rc = RTHandleTableAlloc(hHT, (void *)((uintptr_t)&i + (uintptr_t)hExpect * 4), &h); if (RT_FAILURE(rc)) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableAlloc%s: rc=%Rrc!\n", __LINE__, i, pszWithCtx, rc); g_cErrors++; } else if (h != hExpect) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableAlloc%s: h=%u hExpect=%u! - abort sub-test\n", __LINE__, i, pszWithCtx, h, hExpect); g_cErrors++; break; } if (i >= cDelta + uBase) { /* lookup */ for (uint32_t j = i - cDelta; j <= i; j++) { uint32_t hLookup = ((j - uBase) % (c - uBase)) + uBase; void *pvExpect = (void *)((uintptr_t)&i + (uintptr_t)hLookup * 4); void *pvObj; if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT) pvObj = RTHandleTableLookupWithCtx(hHT, hLookup, NULL); else pvObj = RTHandleTableLookup(hHT, hLookup); if (pvObj != pvExpect) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, j=%d, RTHandleTableLookup%s(,%u,): pvObj=%p expected %p!\n", __LINE__, i, j, pszWithCtx, hLookup, pvObj, pvExpect); g_cErrors++; } else if ( (fFlags & RTHANDLETABLE_FLAGS_CONTEXT) && RTHandleTableLookupWithCtx(hHT, hLookup, &i)) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, j=%d, RTHandleTableLookupWithCtx: succeeded with bad context\n", __LINE__, i, j); g_cErrors++; } } /* free */ uint32_t hFree = ((i - uBase - cDelta) % (c - uBase)) + uBase; void *pvExpect = (void *)((uintptr_t)&i + (uintptr_t)hFree * 4); void *pvObj; if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT) pvObj = RTHandleTableFreeWithCtx(hHT, hFree, NULL); else pvObj = RTHandleTableFree(hHT, hFree); if (pvObj != pvExpect) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableFree%s: pvObj=%p expected %p!\n", __LINE__, i, pszWithCtx, pvObj, pvExpect); g_cErrors++; } else if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT ? RTHandleTableLookupWithCtx(hHT, hFree, NULL) || RTHandleTableFreeWithCtx(hHT, hFree, NULL) : RTHandleTableLookup(hHT, hFree) || RTHandleTableFree(hHT, hFree)) { RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableLookup/Free%s: succeeded after free\n", __LINE__, i, pszWithCtx); g_cErrors++; } } if (!(i % (cUnitsPerDot * 2))) { RTPrintf("."); RTStrmFlush(g_pStdOut); } } RTPrintf("\n"); /* finally, destroy the table (note that there are 128 entries in it). */ cRetainerCalls = 0; uint32_t cDeleteCalls = 0; rc = RTHandleTableDestroy(hHT, fCallbacks ? tstHandleTableTest1Delete : NULL, fCallbacks ? &cDeleteCalls : NULL); if (RT_FAILURE(rc)) { RTPrintf("tstHandleTable: FAILURE (%d) - RTHandleTableDestroy failed, %Rrc!\n", __LINE__, rc); g_cErrors++; } return 0; }
/** * Entry point. */ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) { RT_NOREF1(envp); /* * Init runtime and the test environment. */ RTTEST hTest; RTEXITCODE rcExit = RTTestInitExAndCreate(argc, &argv, RTR3INIT_FLAGS_SUPLIB, "tstVMM", &hTest); if (rcExit != RTEXITCODE_SUCCESS) return rcExit; /* * Parse arguments. */ static const RTGETOPTDEF s_aOptions[] = { { "--cpus", 'c', RTGETOPT_REQ_UINT8 }, { "--test", 't', RTGETOPT_REQ_STRING }, { "--stat", 's', RTGETOPT_REQ_NOTHING }, }; enum { kTstVMMTest_VMM, kTstVMMTest_TM, kTstVMMTest_MSRs, kTstVMMTest_KnownMSRs, kTstVMMTest_MSRExperiments } enmTestOpt = kTstVMMTest_VMM; int ch; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); while ((ch = RTGetOpt(&GetState, &ValueUnion))) { switch (ch) { case 'c': g_cCpus = ValueUnion.u8; break; case 't': if (!strcmp("vmm", ValueUnion.psz)) enmTestOpt = kTstVMMTest_VMM; else if (!strcmp("tm", ValueUnion.psz)) enmTestOpt = kTstVMMTest_TM; else if (!strcmp("msr", ValueUnion.psz) || !strcmp("msrs", ValueUnion.psz)) enmTestOpt = kTstVMMTest_MSRs; else if (!strcmp("known-msr", ValueUnion.psz) || !strcmp("known-msrs", ValueUnion.psz)) enmTestOpt = kTstVMMTest_KnownMSRs; else if (!strcmp("msr-experiments", ValueUnion.psz)) enmTestOpt = kTstVMMTest_MSRExperiments; else { RTPrintf("tstVMM: unknown test: '%s'\n", ValueUnion.psz); return 1; } break; case 's': g_fStat = true; break; case 'h': RTPrintf("usage: tstVMM [--cpus|-c cpus] [-s] [--test <vmm|tm|msrs|known-msrs>]\n"); return 1; case 'V': RTPrintf("$Revision$\n"); return 0; default: return RTGetOptPrintError(ch, &ValueUnion); } } /* * Create the test VM. */ RTPrintf(TESTCASE ": Initializing...\n"); PVM pVM; PUVM pUVM; int rc = VMR3Create(g_cCpus, NULL, NULL, NULL, tstVMMConfigConstructor, NULL, &pVM, &pUVM); if (RT_SUCCESS(rc)) { PDMR3LdrEnumModules(pVM, tstVMMLdrEnum, NULL); RTStrmFlush(g_pStdOut); RTThreadSleep(256); /* * Do the requested testing. */ switch (enmTestOpt) { case kTstVMMTest_VMM: { RTTestSub(hTest, "VMM"); rc = VMR3ReqCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)VMMDoTest, 1, pVM); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMMDoTest failed: rc=%Rrc\n", rc); if (g_fStat) STAMR3Dump(pUVM, "*"); break; } case kTstVMMTest_TM: { RTTestSub(hTest, "TM"); for (VMCPUID idCpu = 1; idCpu < g_cCpus; idCpu++) { rc = VMR3ReqCallNoWaitU(pUVM, idCpu, (PFNRT)tstTMWorker, 2, pVM, hTest); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMR3ReqCall failed: rc=%Rrc\n", rc); } rc = VMR3ReqCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)tstTMWorker, 2, pVM, hTest); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMMDoTest failed: rc=%Rrc\n", rc); if (g_fStat) STAMR3Dump(pUVM, "*"); break; } case kTstVMMTest_MSRs: { RTTestSub(hTest, "MSRs"); if (g_cCpus == 1) { rc = VMR3ReqCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)VMMDoBruteForceMsrs, 1, pVM); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMMDoBruteForceMsrs failed: rc=%Rrc\n", rc); } else RTTestFailed(hTest, "The MSR test can only be run with one VCpu!\n"); break; } case kTstVMMTest_KnownMSRs: { RTTestSub(hTest, "Known MSRs"); if (g_cCpus == 1) { rc = VMR3ReqCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)VMMDoKnownMsrs, 1, pVM); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMMDoKnownMsrs failed: rc=%Rrc\n", rc); } else RTTestFailed(hTest, "The MSR test can only be run with one VCpu!\n"); break; } case kTstVMMTest_MSRExperiments: { RTTestSub(hTest, "MSR Experiments"); if (g_cCpus == 1) { rc = VMR3ReqCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)VMMDoMsrExperiments, 1, pVM); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMMDoMsrExperiments failed: rc=%Rrc\n", rc); } else RTTestFailed(hTest, "The MSR test can only be run with one VCpu!\n"); break; } } /* * Cleanup. */ rc = VMR3PowerOff(pUVM); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMR3PowerOff failed: rc=%Rrc\n", rc); rc = VMR3Destroy(pUVM); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMR3Destroy failed: rc=%Rrc\n", rc); VMR3ReleaseUVM(pUVM); } else RTTestFailed(hTest, "VMR3Create failed: rc=%Rrc\n", rc); return RTTestSummaryAndDestroy(hTest); }
/** * 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); }
/** * Entry point. */ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) { RT_NOREF1(envp); RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB); RTPrintf(TESTCASE ": TESTING...\n"); RTStrmFlush(g_pStdOut); /* * Create empty VM. */ PUVM pUVM; int rc = VMR3Create(1, NULL, NULL, NULL, tstVMREQConfigConstructor, NULL, NULL, &pUVM); if (RT_SUCCESS(rc)) { /* * Do testing. */ uint64_t u64StartTS = RTTimeNanoTS(); RTTHREAD Thread0; rc = RTThreadCreate(&Thread0, Thread, pUVM, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "REQ1"); if (RT_SUCCESS(rc)) { RTTHREAD Thread1; rc = RTThreadCreate(&Thread1, Thread, pUVM, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "REQ1"); if (RT_SUCCESS(rc)) { int rcThread1; rc = RTThreadWait(Thread1, RT_INDEFINITE_WAIT, &rcThread1); if (RT_FAILURE(rc)) { RTPrintf(TESTCASE ": RTThreadWait(Thread1,,) failed, rc=%Rrc\n", rc); g_cErrors++; } if (RT_FAILURE(rcThread1)) g_cErrors++; } else { RTPrintf(TESTCASE ": RTThreadCreate(&Thread1,,,,) failed, rc=%Rrc\n", rc); g_cErrors++; } int rcThread0; rc = RTThreadWait(Thread0, RT_INDEFINITE_WAIT, &rcThread0); if (RT_FAILURE(rc)) { RTPrintf(TESTCASE ": RTThreadWait(Thread1,,) failed, rc=%Rrc\n", rc); g_cErrors++; } if (RT_FAILURE(rcThread0)) g_cErrors++; } else { RTPrintf(TESTCASE ": RTThreadCreate(&Thread0,,,,) failed, rc=%Rrc\n", rc); g_cErrors++; } uint64_t u64ElapsedTS = RTTimeNanoTS() - u64StartTS; RTPrintf(TESTCASE ": %llu ns elapsed\n", u64ElapsedTS); RTStrmFlush(g_pStdOut); /* * Print stats. */ STAMR3Print(pUVM, "/VM/Req/*"); /* * Testing va_list fun. */ RTPrintf(TESTCASE ": va_list argument test...\n"); RTStrmFlush(g_pStdOut); PassVA(pUVM, "hello %s", "world"); VMR3AtRuntimeErrorRegister(pUVM, MyAtRuntimeError, (void *)"user argument"); VMSetRuntimeError(VMR3GetVM(pUVM), 0 /*fFlags*/, "enum", "some %s string", "error"); /* * Cleanup. */ rc = VMR3PowerOff(pUVM); if (!RT_SUCCESS(rc)) { RTPrintf(TESTCASE ": error: failed to power off vm! rc=%Rrc\n", rc); g_cErrors++; } rc = VMR3Destroy(pUVM); if (!RT_SUCCESS(rc)) { RTPrintf(TESTCASE ": error: failed to destroy vm! rc=%Rrc\n", rc); g_cErrors++; } VMR3ReleaseUVM(pUVM); } else { RTPrintf(TESTCASE ": fatal error: failed to create vm! rc=%Rrc\n", rc); g_cErrors++; } /* * Summary and return. */ if (!g_cErrors) RTPrintf(TESTCASE ": SUCCESS\n"); else RTPrintf(TESTCASE ": FAILURE - %d errors\n", g_cErrors); return !!g_cErrors; }
int main(int argc, char **argv) { int rc = RTR3InitExe(argc, &argv, 0); AssertReleaseRCReturn(rc, 1); /* * Warmup tests. */ RTPrintf("tstDarwinKeyboard: Warmup...\n"); RTTimeNanoTS(); DarwinGrabKeyboard(true); DarwinReleaseKeyboard(); RTTimeNanoTS(); DarwinGrabKeyboard(true); DarwinReleaseKeyboard(); /* Test these too: unsigned DarwinKeycodeToSet1Scancode(unsigned uKeyCode); UInt32 DarwinAdjustModifierMask(UInt32 fModifiers); unsigned DarwinModifierMaskToSet1Scancode(UInt32 fModifiers); unsigned DarwinModifierMaskToDarwinKeycode(UInt32 fModifiers); UInt32 DarwinKeyCodeToDarwinModifierMask(unsigned uKeyCode); unsigned DarwinEventToSet1Scancode(EventRef Event, UInt32 *pfCurKeyModifiers); void DarwinDisableGlobalHotKeys(bool fDisable); */ /* * Grab and release the keyboard a lot of times and time it. * We're looking both at performance and for memory and reference leaks here. */ RTPrintf("tstDarwinKeyboard: Profiling Grab and Release"); RTStrmFlush(g_pStdOut); const uint64_t u64Start = RTTimeNanoTS(); uint64_t u64Grab = 0; uint64_t u64Release = 0; unsigned i; for (i = 0; i < 20; i++) { uint64_t u64 = RTTimeNanoTS(); DarwinGrabKeyboard(argc != 1); u64Grab += RTTimeNanoTS() - u64; u64 = RTTimeNanoTS(); DarwinReleaseKeyboard(); u64Release += RTTimeNanoTS() - u64; if ((i % 10) == 0) { RTPrintf("."); RTStrmFlush(g_pStdOut); } } const uint64_t u64Elapsed = RTTimeNanoTS() - u64Start; RTPrintf("\n" "tstDarwinKeyboard: %u times in %RU64 ms - %RU64 ms per call\n", i, u64Elapsed / 1000000, (u64Elapsed / i) / 1000000); RTPrintf("tstDarwinKeyboard: DarwinGrabKeyboard: %RU64 ms total - %RU64 ms per call\n", u64Grab / 1000000, (u64Grab / i) / 1000000); RTPrintf("tstDarwinKeyboard: DarwinReleaseKeyboard: %RU64 ms total - %RU64 ms per call\n", u64Release / 1000000, (u64Release / i) / 1000000); return 0; }
int main(int argc, char **argv) { /* * Init runtime and the test environment. */ int rc = RTR3InitAndSUPLib(); if (RT_FAILURE(rc)) { RTPrintf("tstVMM: RTR3InitAndSUPLib failed: %Rrc\n", rc); return 1; } RTTEST hTest; rc = RTTestCreate("tstVMM", &hTest); if (RT_FAILURE(rc)) { RTPrintf("tstVMM: RTTestCreate failed: %Rrc\n", rc); return 1; } /* * Parse arguments. */ static const RTGETOPTDEF s_aOptions[] = { { "--cpus", 'c', RTGETOPT_REQ_UINT8 }, { "--test", 't', RTGETOPT_REQ_STRING }, }; enum { kTstVMMTest_VMM, kTstVMMTest_TM } enmTestOpt = kTstVMMTest_VMM; int ch; int i = 1; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); while ((ch = RTGetOpt(&GetState, &ValueUnion))) { switch (ch) { case 'c': g_cCpus = ValueUnion.u8; break; case 't': if (!strcmp("vmm", ValueUnion.psz)) enmTestOpt = kTstVMMTest_VMM; else if (!strcmp("tm", ValueUnion.psz)) enmTestOpt = kTstVMMTest_TM; else { RTPrintf("tstVMM: unknown test: '%s'\n", ValueUnion.psz); return 1; } break; case 'h': RTPrintf("usage: tstVMM [--cpus|-c cpus] [--test <vmm|tm>]\n"); return 1; case 'V': RTPrintf("$Revision: $\n"); return 0; default: return RTGetOptPrintError(ch, &ValueUnion); } } /* * Create the test VM. */ RTPrintf(TESTCASE ": Initializing...\n"); PVM pVM; rc = VMR3Create(g_cCpus, NULL, NULL, NULL, tstVMMConfigConstructor, NULL, &pVM); if (RT_SUCCESS(rc)) { PDMR3LdrEnumModules(pVM, tstVMMLdrEnum, NULL); RTStrmFlush(g_pStdOut); RTThreadSleep(256); /* * Do the requested testing. */ switch (enmTestOpt) { case kTstVMMTest_VMM: { RTTestSub(hTest, "VMM"); rc = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)VMMDoTest, 1, pVM); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMMDoTest failed: rc=%Rrc\n", rc); break; } case kTstVMMTest_TM: { RTTestSub(hTest, "TM"); for (VMCPUID idCpu = 1; idCpu < g_cCpus; idCpu++) { rc = VMR3ReqCallNoWait(pVM, idCpu, (PFNRT)tstTMWorker, 2, pVM, hTest); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMR3ReqCall failed: rc=%Rrc\n", rc); } rc = VMR3ReqCallWait(pVM, 0 /*idDstCpu*/, (PFNRT)tstTMWorker, 2, pVM, hTest); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMMDoTest failed: rc=%Rrc\n", rc); break; } } STAMR3Dump(pVM, "*"); /* * Cleanup. */ rc = VMR3PowerOff(pVM); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMR3PowerOff failed: rc=%Rrc\n", rc); rc = VMR3Destroy(pVM); if (RT_FAILURE(rc)) RTTestFailed(hTest, "VMR3Destroy failed: rc=%Rrc\n", rc); } else RTTestFailed(hTest, "VMR3Create failed: rc=%Rrc\n", rc); return RTTestSummaryAndDestroy(hTest); }
/* execute the switch. */ VMMR3DECL(int) VMMDoTest(PVM pVM) { #if 1 PVMCPU pVCpu = &pVM->aCpus[0]; #ifdef NO_SUPCALLR0VMM RTPrintf("NO_SUPCALLR0VMM\n"); return VINF_SUCCESS; #endif /* * Setup stack for calling VMMGCEntry(). */ RTRCPTR RCPtrEP; int rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "VMMGCEntry", &RCPtrEP); if (RT_SUCCESS(rc)) { RTPrintf("VMM: VMMGCEntry=%RRv\n", RCPtrEP); /* * Test various crashes which we must be able to recover from. */ vmmR3DoTrapTest(pVM, 0x3, 0, VINF_EM_DBG_HYPER_ASSERTION, 0xf0f0f0f0, "vmmGCTestTrap3_FaultEIP", "int3"); vmmR3DoTrapTest(pVM, 0x3, 1, VINF_EM_DBG_HYPER_ASSERTION, 0xf0f0f0f0, "vmmGCTestTrap3_FaultEIP", "int3 WP"); #if defined(DEBUG_bird) /* guess most people would like to skip these since they write to com1. */ vmmR3DoTrapTest(pVM, 0x8, 0, VERR_TRPM_PANIC, 0x00000000, "vmmGCTestTrap8_FaultEIP", "#DF [#PG]"); SELMR3Relocate(pVM); /* this resets the busy flag of the Trap 08 TSS */ bool f; rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "DoubleFault", &f); #if !defined(DEBUG_bird) if (RT_SUCCESS(rc) && f) #endif { /* see triple fault warnings in SELM and VMMGC.cpp. */ vmmR3DoTrapTest(pVM, 0x8, 1, VERR_TRPM_PANIC, 0x00000000, "vmmGCTestTrap8_FaultEIP", "#DF [#PG] WP"); SELMR3Relocate(pVM); /* this resets the busy flag of the Trap 08 TSS */ } #endif vmmR3DoTrapTest(pVM, 0xd, 0, VERR_TRPM_DONT_PANIC, 0xf0f0f0f0, "vmmGCTestTrap0d_FaultEIP", "ltr #GP"); ///@todo find a better \#GP case, on intel ltr will \#PF (busy update?) and not \#GP. //vmmR3DoTrapTest(pVM, 0xd, 1, VERR_TRPM_DONT_PANIC, 0xf0f0f0f0, "vmmGCTestTrap0d_FaultEIP", "ltr #GP WP"); vmmR3DoTrapTest(pVM, 0xe, 0, VERR_TRPM_DONT_PANIC, 0x00000000, "vmmGCTestTrap0e_FaultEIP", "#PF (NULL)"); vmmR3DoTrapTest(pVM, 0xe, 1, VERR_TRPM_DONT_PANIC, 0x00000000, "vmmGCTestTrap0e_FaultEIP", "#PF (NULL) WP"); vmmR3DoTrapTest(pVM, 0xe, 2, VINF_SUCCESS, 0x00000000, NULL, "#PF w/Tmp Handler"); /* This test is no longer relevant as fs and gs are loaded with NULL selectors and we will always return to HC if a #GP occurs while returning to guest code. vmmR3DoTrapTest(pVM, 0xe, 4, VINF_SUCCESS, 0x00000000, NULL, "#PF w/Tmp Handler and bad fs"); */ /* * Set a debug register and perform a context switch. */ rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0); if (rc != VINF_SUCCESS) { RTPrintf("VMM: Nop test failed, rc=%Rrc not VINF_SUCCESS\n", rc); return rc; } /* a harmless breakpoint */ RTPrintf("VMM: testing hardware bp at 0x10000 (not hit)\n"); DBGFADDRESS Addr; DBGFR3AddrFromFlat(pVM, &Addr, 0x10000); RTUINT iBp0; rc = DBGFR3BpSetReg(pVM, &Addr, 0, ~(uint64_t)0, X86_DR7_RW_EO, 1, &iBp0); AssertReleaseRC(rc); rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0); if (rc != VINF_SUCCESS) { RTPrintf("VMM: DR0=0x10000 test failed with rc=%Rrc!\n", rc); return rc; } /* a bad one at VMMGCEntry */ RTPrintf("VMM: testing hardware bp at VMMGCEntry (hit)\n"); DBGFR3AddrFromFlat(pVM, &Addr, RCPtrEP); RTUINT iBp1; rc = DBGFR3BpSetReg(pVM, &Addr, 0, ~(uint64_t)0, X86_DR7_RW_EO, 1, &iBp1); AssertReleaseRC(rc); rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0); if (rc != VINF_EM_DBG_HYPER_BREAKPOINT) { RTPrintf("VMM: DR1=VMMGCEntry test failed with rc=%Rrc! expected VINF_EM_RAW_BREAKPOINT_HYPER\n", rc); return rc; } /* resume the breakpoint */ RTPrintf("VMM: resuming hyper after breakpoint\n"); CPUMSetHyperEFlags(pVCpu, CPUMGetHyperEFlags(pVCpu) | X86_EFL_RF); rc = VMMR3ResumeHyper(pVM, pVCpu); if (rc != VINF_SUCCESS) { RTPrintf("VMM: failed to resume on hyper breakpoint, rc=%Rrc = KNOWN BUG\n", rc); /** @todo fix VMMR3ResumeHyper */ return rc; } /* engage the breakpoint again and try single stepping. */ RTPrintf("VMM: testing hardware bp at VMMGCEntry + stepping\n"); rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0); if (rc != VINF_EM_DBG_HYPER_BREAKPOINT) { RTPrintf("VMM: DR1=VMMGCEntry test failed with rc=%Rrc! expected VINF_EM_RAW_BREAKPOINT_HYPER\n", rc); return rc; } RTGCUINTREG OldPc = CPUMGetHyperEIP(pVCpu); RTPrintf("%RGr=>", OldPc); unsigned i; for (i = 0; i < 8; i++) { CPUMSetHyperEFlags(pVCpu, CPUMGetHyperEFlags(pVCpu) | X86_EFL_TF | X86_EFL_RF); rc = VMMR3ResumeHyper(pVM, pVCpu); if (rc != VINF_EM_DBG_HYPER_STEPPED) { RTPrintf("\nVMM: failed to step on hyper breakpoint, rc=%Rrc\n", rc); return rc; } RTGCUINTREG Pc = CPUMGetHyperEIP(pVCpu); RTPrintf("%RGr=>", Pc); if (Pc == OldPc) { RTPrintf("\nVMM: step failed, PC: %RGr -> %RGr\n", OldPc, Pc); return VERR_GENERAL_FAILURE; } OldPc = Pc; } RTPrintf("ok\n"); /* done, clear it */ if ( RT_FAILURE(DBGFR3BpClear(pVM, iBp0)) || RT_FAILURE(DBGFR3BpClear(pVM, iBp1))) { RTPrintf("VMM: Failed to clear breakpoints!\n"); return VERR_GENERAL_FAILURE; } rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0); if (rc != VINF_SUCCESS) { RTPrintf("VMM: NOP failed, rc=%Rrc\n", rc); return rc; } /* * Interrupt masking. */ RTPrintf("VMM: interrupt masking...\n"); RTStrmFlush(g_pStdOut); RTThreadSleep(250); for (i = 0; i < 10000; i++) { uint64_t StartTick = ASMReadTSC(); rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_INTERRUPT_MASKING, 0); if (rc != VINF_SUCCESS) { RTPrintf("VMM: Interrupt masking failed: rc=%Rrc\n", rc); return rc; } uint64_t Ticks = ASMReadTSC() - StartTick; if (Ticks < (SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage) / 10000)) RTPrintf("Warning: Ticks=%RU64 (< %RU64)\n", Ticks, SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage) / 10000); } /* * Interrupt forwarding. */ CPUMSetHyperState(pVCpu, pVM->vmm.s.pfnCallTrampolineRC, pVCpu->vmm.s.pbEMTStackBottomRC, 0, 0); CPUMPushHyper(pVCpu, 0); CPUMPushHyper(pVCpu, VMMGC_DO_TESTCASE_HYPER_INTERRUPT); CPUMPushHyper(pVCpu, pVM->pVMRC); CPUMPushHyper(pVCpu, 3 * sizeof(RTRCPTR)); /* stack frame size */ CPUMPushHyper(pVCpu, RCPtrEP); /* what to call */ Log(("trampoline=%x\n", pVM->vmm.s.pfnCallTrampolineRC)); /* * Switch and do da thing. */ RTPrintf("VMM: interrupt forwarding...\n"); RTStrmFlush(g_pStdOut); RTThreadSleep(250); i = 0; uint64_t tsBegin = RTTimeNanoTS(); uint64_t TickStart = ASMReadTSC(); Assert(CPUMGetHyperCR3(pVCpu) && CPUMGetHyperCR3(pVCpu) == PGMGetHyperCR3(pVCpu)); do { rc = SUPR3CallVMMR0Fast(pVM->pVMR0, VMMR0_DO_RAW_RUN, 0); if (RT_LIKELY(rc == VINF_SUCCESS)) rc = pVCpu->vmm.s.iLastGZRc; if (RT_FAILURE(rc)) { Log(("VMM: GC returned fatal %Rra in iteration %d\n", rc, i)); VMMR3FatalDump(pVM, pVCpu, rc); return rc; } i++; if (!(i % 32)) Log(("VMM: iteration %d, esi=%08x edi=%08x ebx=%08x\n", i, CPUMGetHyperESI(pVCpu), CPUMGetHyperEDI(pVCpu), CPUMGetHyperEBX(pVCpu))); } while (rc == VINF_EM_RAW_INTERRUPT_HYPER); uint64_t TickEnd = ASMReadTSC(); uint64_t tsEnd = RTTimeNanoTS(); uint64_t Elapsed = tsEnd - tsBegin; uint64_t PerIteration = Elapsed / (uint64_t)i; uint64_t cTicksElapsed = TickEnd - TickStart; uint64_t cTicksPerIteration = cTicksElapsed / (uint64_t)i; RTPrintf("VMM: %8d interrupts in %11llu ns (%11llu ticks), %10llu ns/iteration (%11llu ticks)\n", i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration); Log(("VMM: %8d interrupts in %11llu ns (%11llu ticks), %10llu ns/iteration (%11llu ticks)\n", i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration)); /* * These forced actions are not necessary for the test and trigger breakpoints too. */ VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_TRPM_SYNC_IDT); VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_SELM_SYNC_TSS); /* * Profile switching. */ RTPrintf("VMM: profiling switcher...\n"); Log(("VMM: profiling switcher...\n")); uint64_t TickMin = ~0; tsBegin = RTTimeNanoTS(); TickStart = ASMReadTSC(); Assert(CPUMGetHyperCR3(pVCpu) && CPUMGetHyperCR3(pVCpu) == PGMGetHyperCR3(pVCpu)); for (i = 0; i < 1000000; i++) { CPUMSetHyperState(pVCpu, pVM->vmm.s.pfnCallTrampolineRC, pVCpu->vmm.s.pbEMTStackBottomRC, 0, 0); CPUMPushHyper(pVCpu, 0); CPUMPushHyper(pVCpu, VMMGC_DO_TESTCASE_NOP); CPUMPushHyper(pVCpu, pVM->pVMRC); CPUMPushHyper(pVCpu, 3 * sizeof(RTRCPTR)); /* stack frame size */ CPUMPushHyper(pVCpu, RCPtrEP); /* what to call */ uint64_t TickThisStart = ASMReadTSC(); rc = SUPR3CallVMMR0Fast(pVM->pVMR0, VMMR0_DO_RAW_RUN, 0); if (RT_LIKELY(rc == VINF_SUCCESS)) rc = pVCpu->vmm.s.iLastGZRc; uint64_t TickThisElapsed = ASMReadTSC() - TickThisStart; if (RT_FAILURE(rc)) { Log(("VMM: GC returned fatal %Rra in iteration %d\n", rc, i)); VMMR3FatalDump(pVM, pVCpu, rc); return rc; } if (TickThisElapsed < TickMin) TickMin = TickThisElapsed; } TickEnd = ASMReadTSC(); tsEnd = RTTimeNanoTS(); Elapsed = tsEnd - tsBegin; PerIteration = Elapsed / (uint64_t)i; cTicksElapsed = TickEnd - TickStart; cTicksPerIteration = cTicksElapsed / (uint64_t)i; RTPrintf("VMM: %8d cycles in %11llu ns (%11lld ticks), %10llu ns/iteration (%11lld ticks) Min %11lld ticks\n", i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration, TickMin); Log(("VMM: %8d cycles in %11llu ns (%11lld ticks), %10llu ns/iteration (%11lld ticks) Min %11lld ticks\n", i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration, TickMin)); rc = VINF_SUCCESS; } else AssertMsgFailed(("Failed to resolved VMMGC.gc::VMMGCEntry(), rc=%Rrc\n", rc)); #endif return rc; }
int main(int argc, char **argv) { RTR3InitExe(argc, &argv, 0); /* * Parse arguments. */ static const RTGETOPTDEF s_aOptions[] = { { "--iterations", 'i', RTGETOPT_REQ_UINT32 }, { "--num-pages", 'n', RTGETOPT_REQ_UINT32 }, { "--page-at-a-time", 'c', RTGETOPT_REQ_UINT32 }, { "--page-file", 'f', RTGETOPT_REQ_STRING }, { "--offset", 'o', RTGETOPT_REQ_UINT64 }, }; const char *pszPageFile = NULL; uint64_t offPageFile = 0; uint32_t cIterations = 1; uint32_t cPagesAtATime = 1; RTGETOPTUNION Val; RTGETOPTSTATE State; int rc = RTGetOptInit(&State, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0); AssertRCReturn(rc, 1); while ((rc = RTGetOpt(&State, &Val))) { switch (rc) { case 'n': g_cPages = Val.u32; if (g_cPages * PAGE_SIZE * 4 / (PAGE_SIZE * 4) != g_cPages) return Error("The specified page count is too high: %#x (%#llx bytes)\n", g_cPages, (uint64_t)g_cPages * PAGE_SHIFT); if (g_cPages < 1) return Error("The specified page count is too low: %#x\n", g_cPages); break; case 'i': cIterations = Val.u32; if (cIterations < 1) return Error("The number of iterations must be 1 or higher\n"); break; case 'c': cPagesAtATime = Val.u32; if (cPagesAtATime < 1 || cPagesAtATime > 10240) return Error("The specified pages-at-a-time count is out of range: %#x\n", cPagesAtATime); break; case 'f': pszPageFile = Val.psz; break; case 'o': offPageFile = Val.u64; break; case 'O': offPageFile = Val.u64 * PAGE_SIZE; break; case 'h': RTPrintf("syntax: tstCompressionBenchmark [options]\n" "\n" "Options:\n" " -h, --help\n" " Show this help page\n" " -i, --iterations <num>\n" " The number of iterations.\n" " -n, --num-pages <pages>\n" " The number of pages.\n" " -c, --pages-at-a-time <pages>\n" " Number of pages at a time.\n" " -f, --page-file <filename>\n" " File or device to read the page from. The default\n" " is to generate some garbage.\n" " -o, --offset <file-offset>\n" " Offset into the page file to start reading at.\n"); return 0; case 'V': RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr()); return 0; default: return RTGetOptPrintError(rc, &Val); } } g_cbPages = g_cPages * PAGE_SIZE; uint64_t cbTotal = (uint64_t)g_cPages * PAGE_SIZE * cIterations; uint64_t cbTotalKB = cbTotal / _1K; if (cbTotal / cIterations != g_cbPages) return Error("cPages * cIterations -> overflow\n"); /* * Gather the test memory. */ if (pszPageFile) { size_t cbFile; rc = RTFileReadAllEx(pszPageFile, offPageFile, g_cbPages, RTFILE_RDALL_O_DENY_NONE, (void **)&g_pabSrc, &cbFile); if (RT_FAILURE(rc)) return Error("Error reading %zu bytes from %s at %llu: %Rrc\n", g_cbPages, pszPageFile, offPageFile, rc); if (cbFile != g_cbPages) return Error("Error reading %zu bytes from %s at %llu: got %zu bytes\n", g_cbPages, pszPageFile, offPageFile, cbFile); } else { g_pabSrc = (uint8_t *)RTMemAlloc(g_cbPages); if (g_pabSrc) { /* Just fill it with something - warn about the low quality of the something. */ RTPrintf("tstCompressionBenchmark: WARNING! No input file was specified so the source\n" "buffer will be filled with generated data of questionable quality.\n"); #ifdef RT_OS_LINUX RTPrintf("To get real RAM on linux: sudo dd if=/dev/mem ... \n"); #endif uint8_t *pb = g_pabSrc; uint8_t *pbEnd = &g_pabSrc[g_cbPages]; for (; pb != pbEnd; pb += 16) { char szTmp[17]; RTStrPrintf(szTmp, sizeof(szTmp), "aaaa%08Xzzzz", (uint32_t)(uintptr_t)pb); memcpy(pb, szTmp, 16); } } } g_pabDecompr = (uint8_t *)RTMemAlloc(g_cbPages); g_cbComprAlloc = RT_MAX(g_cbPages * 2, 256 * PAGE_SIZE); g_pabCompr = (uint8_t *)RTMemAlloc(g_cbComprAlloc); if (!g_pabSrc || !g_pabDecompr || !g_pabCompr) return Error("failed to allocate memory buffers (g_cPages=%#x)\n", g_cPages); /* * Double loop compressing and uncompressing the data, where the outer does * the specified number of iterations while the inner applies the different * compression algorithms. */ struct { /** The time spent decompressing. */ uint64_t cNanoDecompr; /** The time spent compressing. */ uint64_t cNanoCompr; /** The size of the compressed data. */ uint64_t cbCompr; /** First error. */ int rc; /** The compression style: block or stream. */ bool fBlock; /** Compression type. */ RTZIPTYPE enmType; /** Compression level. */ RTZIPLEVEL enmLevel; /** Method name. */ const char *pszName; } aTests[] = { { 0, 0, 0, VINF_SUCCESS, false, RTZIPTYPE_STORE, RTZIPLEVEL_DEFAULT, "RTZip/Store" }, { 0, 0, 0, VINF_SUCCESS, false, RTZIPTYPE_LZF, RTZIPLEVEL_DEFAULT, "RTZip/LZF" }, /* { 0, 0, 0, VINF_SUCCESS, false, RTZIPTYPE_ZLIB, RTZIPLEVEL_DEFAULT, "RTZip/zlib" }, - slow plus it randomly hits VERR_GENERAL_FAILURE atm. */ { 0, 0, 0, VINF_SUCCESS, true, RTZIPTYPE_STORE, RTZIPLEVEL_DEFAULT, "RTZipBlock/Store" }, { 0, 0, 0, VINF_SUCCESS, true, RTZIPTYPE_LZF, RTZIPLEVEL_DEFAULT, "RTZipBlock/LZF" }, { 0, 0, 0, VINF_SUCCESS, true, RTZIPTYPE_LZJB, RTZIPLEVEL_DEFAULT, "RTZipBlock/LZJB" }, { 0, 0, 0, VINF_SUCCESS, true, RTZIPTYPE_LZO, RTZIPLEVEL_DEFAULT, "RTZipBlock/LZO" }, }; RTPrintf("tstCompressionBenchmark: TESTING.."); for (uint32_t i = 0; i < cIterations; i++) { for (uint32_t j = 0; j < RT_ELEMENTS(aTests); j++) { if (RT_FAILURE(aTests[j].rc)) continue; memset(g_pabCompr, 0xaa, g_cbComprAlloc); memset(g_pabDecompr, 0xcc, g_cbPages); g_cbCompr = 0; g_offComprIn = 0; RTPrintf("."); RTStrmFlush(g_pStdOut); /* * Compress it. */ uint64_t NanoTS = RTTimeNanoTS(); if (aTests[j].fBlock) { size_t cbLeft = g_cbComprAlloc; uint8_t const *pbSrcPage = g_pabSrc; uint8_t *pbDstPage = g_pabCompr; for (size_t iPage = 0; iPage < g_cPages; iPage += cPagesAtATime) { AssertBreakStmt(cbLeft > PAGE_SIZE * 4, aTests[j].rc = rc = VERR_BUFFER_OVERFLOW); uint32_t *pcb = (uint32_t *)pbDstPage; pbDstPage += sizeof(uint32_t); cbLeft -= sizeof(uint32_t); size_t cbSrc = RT_MIN(g_cPages - iPage, cPagesAtATime) * PAGE_SIZE; size_t cbDst; rc = RTZipBlockCompress(aTests[j].enmType, aTests[j].enmLevel, 0 /*fFlags*/, pbSrcPage, cbSrc, pbDstPage, cbLeft, &cbDst); if (RT_FAILURE(rc)) { Error("RTZipBlockCompress failed for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc); aTests[j].rc = rc; break; } *pcb = (uint32_t)cbDst; cbLeft -= cbDst; pbDstPage += cbDst; pbSrcPage += cbSrc; } if (RT_FAILURE(rc)) continue; g_cbCompr = pbDstPage - g_pabCompr; } else { PRTZIPCOMP pZipComp; rc = RTZipCompCreate(&pZipComp, NULL, ComprOutCallback, aTests[j].enmType, aTests[j].enmLevel); if (RT_FAILURE(rc)) { Error("Failed to create the compressor for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc); aTests[j].rc = rc; continue; } uint8_t const *pbSrcPage = g_pabSrc; for (size_t iPage = 0; iPage < g_cPages; iPage += cPagesAtATime) { size_t cb = RT_MIN(g_cPages - iPage, cPagesAtATime) * PAGE_SIZE; rc = RTZipCompress(pZipComp, pbSrcPage, cb); if (RT_FAILURE(rc)) { Error("RTZipCompress failed for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc); aTests[j].rc = rc; break; } pbSrcPage += cb; } if (RT_FAILURE(rc)) continue; rc = RTZipCompFinish(pZipComp); if (RT_FAILURE(rc)) { Error("RTZipCompFinish failed for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc); aTests[j].rc = rc; break; } RTZipCompDestroy(pZipComp); } NanoTS = RTTimeNanoTS() - NanoTS; aTests[j].cbCompr += g_cbCompr; aTests[j].cNanoCompr += NanoTS; /* * Decompress it. */ NanoTS = RTTimeNanoTS(); if (aTests[j].fBlock) { uint8_t const *pbSrcPage = g_pabCompr; size_t cbLeft = g_cbCompr; uint8_t *pbDstPage = g_pabDecompr; for (size_t iPage = 0; iPage < g_cPages; iPage += cPagesAtATime) { size_t cbDst = RT_MIN(g_cPages - iPage, cPagesAtATime) * PAGE_SIZE; size_t cbSrc = *(uint32_t *)pbSrcPage; pbSrcPage += sizeof(uint32_t); cbLeft -= sizeof(uint32_t); rc = RTZipBlockDecompress(aTests[j].enmType, 0 /*fFlags*/, pbSrcPage, cbSrc, &cbSrc, pbDstPage, cbDst, &cbDst); if (RT_FAILURE(rc)) { Error("RTZipBlockDecompress failed for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc); aTests[j].rc = rc; break; } pbDstPage += cbDst; cbLeft -= cbSrc; pbSrcPage += cbSrc; } if (RT_FAILURE(rc)) continue; } else { PRTZIPDECOMP pZipDecomp; rc = RTZipDecompCreate(&pZipDecomp, NULL, DecomprInCallback); if (RT_FAILURE(rc)) { Error("Failed to create the decompressor for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc); aTests[j].rc = rc; continue; } uint8_t *pbDstPage = g_pabDecompr; for (size_t iPage = 0; iPage < g_cPages; iPage += cPagesAtATime) { size_t cb = RT_MIN(g_cPages - iPage, cPagesAtATime) * PAGE_SIZE; rc = RTZipDecompress(pZipDecomp, pbDstPage, cb, NULL); if (RT_FAILURE(rc)) { Error("RTZipDecompress failed for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc); aTests[j].rc = rc; break; } pbDstPage += cb; } RTZipDecompDestroy(pZipDecomp); if (RT_FAILURE(rc)) continue; } NanoTS = RTTimeNanoTS() - NanoTS; aTests[j].cNanoDecompr += NanoTS; if (memcmp(g_pabDecompr, g_pabSrc, g_cbPages)) { Error("The compressed data doesn't match the source for '%s' (%#u)\n", aTests[j].pszName, j); aTests[j].rc = VERR_BAD_EXE_FORMAT; continue; } } } if (RT_SUCCESS(rc)) RTPrintf("\n"); /* * Report the results. */ rc = 0; RTPrintf("tstCompressionBenchmark: BEGIN RESULTS\n"); RTPrintf("%-20s Compression Decompression\n", ""); RTPrintf("%-20s In Out Ratio Size In Out\n", "Method"); RTPrintf("%.20s-----------------------------------------------------------------------------------------\n", "---------------------------------------------"); for (uint32_t j = 0; j < RT_ELEMENTS(aTests); j++) { if (RT_SUCCESS(aTests[j].rc)) { unsigned uComprSpeedIn = (unsigned)(cbTotalKB / (long double)aTests[j].cNanoCompr * 1000000000.0); unsigned uComprSpeedOut = (unsigned)(aTests[j].cbCompr / (long double)aTests[j].cNanoCompr * 1000000000.0 / 1024); unsigned uRatio = (unsigned)(aTests[j].cbCompr / cIterations * 100 / g_cbPages); unsigned uDecomprSpeedIn = (unsigned)(aTests[j].cbCompr / (long double)aTests[j].cNanoDecompr * 1000000000.0 / 1024); unsigned uDecomprSpeedOut = (unsigned)(cbTotalKB / (long double)aTests[j].cNanoDecompr * 1000000000.0); RTPrintf("%-20s %'9u KB/s %'9u KB/s %3u%% %'11llu bytes %'9u KB/s %'9u KB/s", aTests[j].pszName, uComprSpeedIn, uComprSpeedOut, uRatio, aTests[j].cbCompr / cIterations, uDecomprSpeedIn, uDecomprSpeedOut); #if 0 RTPrintf(" [%'14llu / %'14llu ns]\n", aTests[j].cNanoCompr / cIterations, aTests[j].cNanoDecompr / cIterations); #else RTPrintf("\n"); #endif } else { RTPrintf("%-20s: %Rrc\n", aTests[j].pszName, aTests[j].rc); rc = 1; } } if (pszPageFile) RTPrintf("Input: %'10zu pages from '%s' starting at offset %'lld (%#llx)\n" " %'11zu bytes\n", g_cPages, pszPageFile, offPageFile, offPageFile, g_cbPages); else RTPrintf("Input: %'10zu pages of generated rubbish %'11zu bytes\n", g_cPages, g_cbPages); /* * Count zero pages in the data set. */ size_t cZeroPages = 0; for (size_t iPage = 0; iPage < g_cPages; iPage++) { if (!ASMMemIsAllU32(&g_pabSrc[iPage * PAGE_SIZE], PAGE_SIZE, 0)) cZeroPages++; } RTPrintf(" %'10zu zero pages (%u %%)\n", cZeroPages, cZeroPages * 100 / g_cPages); /* * A little extension to the test, benchmark relevant CRCs. */ RTPrintf("\n" "tstCompressionBenchmark: Hash/CRC - All In One\n"); tstBenchmarkCRCsAllInOne(g_pabSrc, g_cbPages); RTPrintf("\n" "tstCompressionBenchmark: Hash/CRC - Page by Page\n"); tstBenchmarkCRCsPageByPage(g_pabSrc, g_cbPages); RTPrintf("\n" "tstCompressionBenchmark: Hash/CRC - Zero Page Digest\n"); static uint8_t s_abZeroPg[PAGE_SIZE]; RT_ZERO(s_abZeroPg); tstBenchmarkCRCsAllInOne(s_abZeroPg, PAGE_SIZE); RTPrintf("\n" "tstCompressionBenchmark: Hash/CRC - Zero Half Page Digest\n"); tstBenchmarkCRCsAllInOne(s_abZeroPg, PAGE_SIZE / 2); RTPrintf("tstCompressionBenchmark: END RESULTS\n"); return rc; }
RTDECL(int) RTCrStoreCertExportAsPem(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename) { /* * Validate input. */ AssertReturn(!fFlags, VERR_INVALID_FLAGS); /* * Start the enumeration first as this validates the store handle. */ RTCRSTORECERTSEARCH Search; int rc = RTCrStoreCertFindAll(hStore, &Search); if (RT_SUCCESS(rc)) { /* * Open the file for writing. * * Note! We must use text and no binary here, because the base-64 API * below will use host specific EOL markers, not CRLF as PEM * specifies. */ PRTSTREAM hStrm; rc = RTStrmOpen(pszFilename, "w", &hStrm); if (RT_SUCCESS(rc)) { /* * Enumerate the certificates in the store, writing them out one by one. */ size_t cbBase64 = 0; char *pszBase64 = NULL; PCRTCRCERTCTX pCertCtx; while ((pCertCtx = RTCrStoreCertSearchNext(hStore, &Search)) != NULL) { const char *pszMarker; switch (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK) { case RTCRCERTCTX_F_ENC_X509_DER: pszMarker = "CERTIFICATE"; break; case RTCRCERTCTX_F_ENC_TAF_DER: pszMarker = "TRUST ANCHOR"; break; default: pszMarker = NULL; break; } if (pszMarker && pCertCtx->cbEncoded > 0) { /* * Do the base64 conversion first. */ size_t cchEncoded = RTBase64EncodedLength(pCertCtx->cbEncoded); if (cchEncoded < cbBase64) { /* likely */ } else { size_t cbNew = RT_ALIGN(cchEncoded + 64, 128); void *pvNew = RTMemRealloc(pszBase64, cbNew); if (!pvNew) { rc = VERR_NO_MEMORY; break; } cbBase64 = cbNew; pszBase64 = (char *)pvNew; } rc = RTBase64Encode(pCertCtx->pabEncoded, pCertCtx->cbEncoded, pszBase64, cbBase64, &cchEncoded); if (RT_FAILURE(rc)) break; RTStrmPrintf(hStrm, "-----BEGIN %s-----\n", pszMarker); RTStrmWrite(hStrm, pszBase64, cchEncoded); rc = RTStrmPrintf(hStrm, "\n-----END %s-----\n", pszMarker); if (RT_FAILURE(rc)) break; } RTCrCertCtxRelease(pCertCtx); } if (pCertCtx) RTCrCertCtxRelease(pCertCtx); RTMemFree(pszBase64); /* * Flush the output file before closing. */ int rc2 = RTStrmFlush(hStrm); if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) rc = rc2; RTStrmClearError(hStrm); /** @todo fix RTStrmClose... */ rc2 = RTStrmClose(hStrm); if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) rc = rc2; } int rc2 = RTCrStoreCertSearchDestroy(hStore, &Search); AssertRC(rc2); } return rc; }
RTDECL(RTEXITCODE) RTPathRmCmd(unsigned cArgs, char **papszArgs) { /* * Parse the command line. */ static const RTGETOPTDEF s_aOptions[] = { /* operations */ { "--dirs-and-more", 'd', RTGETOPT_REQ_NOTHING }, { "--force", 'f', RTGETOPT_REQ_NOTHING }, { "--prompt", 'i', RTGETOPT_REQ_NOTHING }, { "--prompt-once", 'I', RTGETOPT_REQ_NOTHING }, { "--interactive", RTPATHRMCMD_OPT_INTERACTIVE, RTGETOPT_REQ_STRING }, { "--one-file-system", RTPATHRMCMD_OPT_ONE_FILE_SYSTEM, RTGETOPT_REQ_NOTHING }, { "--preserve-root", RTPATHRMCMD_OPT_PRESERVE_ROOT, RTGETOPT_REQ_NOTHING }, { "--no-preserve-root", RTPATHRMCMD_OPT_NO_PRESERVE_ROOT, RTGETOPT_REQ_NOTHING }, { "--recursive", 'R', RTGETOPT_REQ_NOTHING }, { "--recursive", 'r', RTGETOPT_REQ_NOTHING }, { "--safe-delete", 'P', RTGETOPT_REQ_NOTHING }, { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, /* IPRT extensions */ { "--machine-readable", RTPATHRMCMD_OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING }, { "--machinereadable", RTPATHRMCMD_OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING }, /* bad long option style */ }; RTGETOPTSTATE GetState; int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc); RTPATHRMCMDOPTS Opts; RT_ZERO(Opts); Opts.fPreserveRoot = true; Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_NONE; RTGETOPTUNION ValueUnion; while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0 && rc != VINF_GETOPT_NOT_OPTION) { switch (rc) { case 'd': Opts.fDirsAndOther = true; break; case 'f': Opts.fForce = true; Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_NONE; break; case 'i': Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ALL; break; case 'I': Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ONCE; break; case RTPATHRMCMD_OPT_INTERACTIVE: if (!strcmp(ValueUnion.psz, "always")) Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ALL; else if (!strcmp(ValueUnion.psz, "once")) Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ONCE; else return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown --interactive option value: '%s'\n", ValueUnion.psz); break; case RTPATHRMCMD_OPT_ONE_FILE_SYSTEM: Opts.fOneFileSystem = true; break; case RTPATHRMCMD_OPT_PRESERVE_ROOT: Opts.fPreserveRoot = true; break; case RTPATHRMCMD_OPT_NO_PRESERVE_ROOT: Opts.fPreserveRoot = false; break; case 'R': case 'r': Opts.fRecursive = true; Opts.fDirsAndOther = true; break; case 'P': Opts.fSafeDelete = true; break; case 'v': Opts.fVerbose = true; break; case RTPATHRMCMD_OPT_MACHINE_READABLE: Opts.fMachineReadable = true; break; case 'h': RTPrintf("Usage: to be written\nOption dump:\n"); for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++) if (RT_C_IS_PRINT(s_aOptions[i].iShort)) RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong); else RTPrintf(" %s\n", s_aOptions[i].pszLong); return RTEXITCODE_SUCCESS; case 'V': RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision()); return RTEXITCODE_SUCCESS; default: return RTGetOptPrintError(rc, &ValueUnion); } } /* * Options we don't support. */ if (Opts.fOneFileSystem) return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --one-file-system option is not yet implemented.\n"); if (Opts.enmInteractive != RTPATHRMCMDINTERACTIVE_NONE) return RTMsgErrorExit(RTEXITCODE_FAILURE, "The -i, -I and --interactive options are not implemented yet.\n"); /* * No files means error. */ if (rc != VINF_GETOPT_NOT_OPTION && !Opts.fForce) return RTMsgErrorExit(RTEXITCODE_FAILURE, "No files or directories specified.\n"); /* * Machine readable init + header. */ if (Opts.fMachineReadable) { rc = RTStrmSetMode(g_pStdOut, true /*fBinary*/, false /*fCurrentCodeSet*/); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTStrmSetMode failed: %Rrc.\n", rc); static const char s_achHeader[] = "hdr_id=rm\0hdr_ver=1"; RTStrmWrite(g_pStdOut, s_achHeader, sizeof(s_achHeader)); } /* * Delete the specified files/dirs/whatever. */ RTEXITCODE rcExit = RTEXITCODE_SUCCESS; while (rc == VINF_GETOPT_NOT_OPTION) { rc = rtPathRmOne(&Opts, ValueUnion.psz); if (RT_FAILURE(rc)) rcExit = RTEXITCODE_FAILURE; /* next */ rc = RTGetOpt(&GetState, &ValueUnion); } if (rc != 0) rcExit = RTGetOptPrintError(rc, &ValueUnion); /* * Terminate the machine readable stuff. */ if (Opts.fMachineReadable) { RTStrmWrite(g_pStdOut, "\0\0\0", 4); rc = RTStrmFlush(g_pStdOut); if (RT_FAILURE(rc) && rcExit == RTEXITCODE_SUCCESS) rcExit = RTEXITCODE_FAILURE; } return rcExit; }