/** * Test automatic cleanup upon destruction. */ static void tst3(void) { RTTestISub("Destroy non-empty pool"); /* * Nothing freed. */ RTMEMPOOL hMemPool; RTTESTI_CHECK_RC_RETV(RTMemPoolCreate(&hMemPool, "test 3a"), VINF_SUCCESS); RTTESTI_CHECK_RETV(RTMemPoolAlloc(hMemPool, 10)); RTTESTI_CHECK_RETV(RTMemPoolAlloc(hMemPool, 20)); RTTESTI_CHECK_RETV(RTMemPoolAlloc(hMemPool, 40)); RTTESTI_CHECK_RETV(RTMemPoolAlloc(hMemPool, 80)); RTTESTI_CHECK_RC_RETV(RTMemPoolDestroy(hMemPool), VINF_SUCCESS); /* * Pseudo random freeing to test list maintenance. */ RTRAND hRand; RTTESTI_CHECK_RC_OK_RETV(RTRandAdvCreateParkMiller(&hRand)); for (uint32_t i = 0; i < 10; i++) { RTTESTI_CHECK_RC_RETV(RTMemPoolCreate(&hMemPool, "test 3b"), VINF_SUCCESS); void *apvHistory[256]; RT_ZERO(apvHistory); uint32_t cBlocks = 0; uint32_t j; for (j = 0; j < RT_ELEMENTS(apvHistory) - i * 7; j++) { RTTESTI_CHECK_RETV(apvHistory[j] = RTMemPoolAlloc(hMemPool, j)); memset(apvHistory[j], 'a', j); cBlocks++; if (RTRandAdvU32Ex(hRand, 0, 4) == 4) { uint32_t iFree = RTRandAdvU32Ex(hRand, 0, j); cBlocks -= apvHistory[iFree] != NULL; RTTESTI_CHECK_RETV(RTMemPoolRelease(hMemPool, apvHistory[iFree]) == 0); apvHistory[iFree] = NULL; } } RTTESTI_CHECK_RC_RETV(RTMemPoolDestroy(hMemPool), VINF_SUCCESS); RTTestIPrintf(RTTESTLVL_INFO, "cBlocks=%u j=%u\n", cBlocks, j); } RTRandAdvDestroy(hRand); }
/** * Check hash and memory performance. */ static void tst2(void) { RTTestISub("Hash performance"); /* * Generate test strings using a specific pseudo random generator. */ size_t cbStrings = 0; char *apszTests[8192]; RTRAND hRand; RTTESTI_CHECK_RC_RETV(RTRandAdvCreateParkMiller(&hRand), VINF_SUCCESS); for (uint32_t i = 0; i < 8192; i++) { char szBuf[8192]; uint32_t cch = RTRandAdvU32Ex(hRand, 3, sizeof(szBuf) - 1); RTRandAdvBytes(hRand, szBuf, cch); szBuf[cch] = '\0'; for (uint32_t off = 0; off < cch; off++) { uint8_t b = szBuf[off]; b &= 0x7f; if (!b || b == 0x7f) b = ' '; else if (RTLocCIsCntrl(b) && b != '\n' && b != '\r' && b != '\t') b += 0x30; szBuf[off] = b; } apszTests[i] = (char *)RTMemDup(szBuf, cch + 1); RTTESTI_CHECK_RETV(apszTests[i] != NULL); cbStrings += cch + 1; } RTRandAdvDestroy(hRand); RTTestIValue("Average string", cbStrings / RT_ELEMENTS(apszTests), RTTESTUNIT_BYTES); /* * Test new insertion first time around. */ RTSTRCACHE hStrCache; RTTESTI_CHECK_RC_RETV(RTStrCacheCreate(&hStrCache, "hash performance"), VINF_SUCCESS); uint64_t nsTsStart = RTTimeNanoTS(); for (uint32_t i = 0; i < RT_ELEMENTS(apszTests); i++) RTTESTI_CHECK_RETV(RTStrCacheEnter(hStrCache, apszTests[i]) != NULL); uint64_t cNsElapsed = RTTimeNanoTS() - nsTsStart; RTTestIValue("First insert", cNsElapsed / RT_ELEMENTS(apszTests), RTTESTUNIT_NS_PER_CALL); /* * Insert existing strings. */ nsTsStart = RTTimeNanoTS(); for (uint32_t i = 0; i < 8192; i++) RTTESTI_CHECK(RTStrCacheEnter(hStrCache, apszTests[i]) != NULL); cNsElapsed = RTTimeNanoTS() - nsTsStart; RTTestIValue("Duplicate insert", cNsElapsed / RT_ELEMENTS(apszTests), RTTESTUNIT_NS_PER_CALL); tstShowStats(hStrCache); RTTESTI_CHECK_RC(RTStrCacheDestroy(hStrCache), VINF_SUCCESS); }
int main(int argc, char *argv[]) { /* * Init runtime. */ RTTEST hTest; int rc = RTTestInitAndCreate("tstRTHeapOffset", &hTest); if (rc) return rc; RTTestBanner(hTest); /* * Create a heap. */ RTTestSub(hTest, "Basics"); static uint8_t s_abMem[128*1024]; RTHEAPOFFSET Heap; RTTESTI_CHECK_RC(rc = RTHeapOffsetInit(&Heap, &s_abMem[1], sizeof(s_abMem) - 1), VINF_SUCCESS); if (RT_FAILURE(rc)) return RTTestSummaryAndDestroy(hTest); /* * Try allocate. */ static struct TstHeapOffsetOps { size_t cb; unsigned uAlignment; void *pvAlloc; unsigned iFreeOrder; } s_aOps[] = { { 16, 0, NULL, 0 }, // 0 { 16, 4, NULL, 1 }, { 16, 8, NULL, 2 }, { 16, 16, NULL, 5 }, { 16, 32, NULL, 4 }, { 32, 0, NULL, 3 }, // 5 { 31, 0, NULL, 6 }, { 1024, 0, NULL, 8 }, { 1024, 32, NULL, 10 }, { 1024, 32, NULL, 12 }, { PAGE_SIZE, PAGE_SIZE, NULL, 13 }, // 10 { 1024, 32, NULL, 9 }, { PAGE_SIZE, 32, NULL, 11 }, { PAGE_SIZE, PAGE_SIZE, NULL, 14 }, { 16, 0, NULL, 15 }, { 9, 0, NULL, 7 }, // 15 { 16, 0, NULL, 7 }, { 36, 0, NULL, 7 }, { 16, 0, NULL, 7 }, { 12344, 0, NULL, 7 }, { 50, 0, NULL, 7 }, // 20 { 16, 0, NULL, 7 }, }; uint32_t i; RTHeapOffsetDump(Heap, (PFNRTHEAPOFFSETPRINTF)RTPrintf); /** @todo Add some detail info output with a signature identical to RTPrintf. */ size_t cbBefore = RTHeapOffsetGetFreeSize(Heap); static char const s_szFill[] = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; /* allocate */ for (i = 0; i < RT_ELEMENTS(s_aOps); i++) { s_aOps[i].pvAlloc = RTHeapOffsetAlloc(Heap, s_aOps[i].cb, s_aOps[i].uAlignment); RTTESTI_CHECK_MSG(s_aOps[i].pvAlloc, ("RTHeapOffsetAlloc(%p, %#x, %#x,) -> NULL i=%d\n", (void *)Heap, s_aOps[i].cb, s_aOps[i].uAlignment, i)); if (!s_aOps[i].pvAlloc) return RTTestSummaryAndDestroy(hTest); memset(s_aOps[i].pvAlloc, s_szFill[i], s_aOps[i].cb); RTTESTI_CHECK_MSG(RT_ALIGN_P(s_aOps[i].pvAlloc, (s_aOps[i].uAlignment ? s_aOps[i].uAlignment : 8)) == s_aOps[i].pvAlloc, ("RTHeapOffsetAlloc(%p, %#x, %#x,) -> %p\n", (void *)Heap, s_aOps[i].cb, s_aOps[i].uAlignment, i)); if (!s_aOps[i].pvAlloc) return RTTestSummaryAndDestroy(hTest); } /* free and allocate the same node again. */ for (i = 0; i < RT_ELEMENTS(s_aOps); i++) { if (!s_aOps[i].pvAlloc) continue; //RTPrintf("debug: i=%d pv=%#x cb=%#zx align=%#zx cbReal=%#zx\n", i, s_aOps[i].pvAlloc, // s_aOps[i].cb, s_aOps[i].uAlignment, RTHeapOffsetSize(Heap, s_aOps[i].pvAlloc)); size_t cbBeforeSub = RTHeapOffsetGetFreeSize(Heap); RTHeapOffsetFree(Heap, s_aOps[i].pvAlloc); size_t cbAfterSubFree = RTHeapOffsetGetFreeSize(Heap); void *pv; pv = RTHeapOffsetAlloc(Heap, s_aOps[i].cb, s_aOps[i].uAlignment); RTTESTI_CHECK_MSG(pv, ("RTHeapOffsetAlloc(%p, %#x, %#x,) -> NULL i=%d\n", (void *)Heap, s_aOps[i].cb, s_aOps[i].uAlignment, i)); if (!pv) return RTTestSummaryAndDestroy(hTest); //RTPrintf("debug: i=%d pv=%p cbReal=%#zx cbBeforeSub=%#zx cbAfterSubFree=%#zx cbAfterSubAlloc=%#zx \n", i, pv, RTHeapOffsetSize(Heap, pv), // cbBeforeSub, cbAfterSubFree, RTHeapOffsetGetFreeSize(Heap)); if (pv != s_aOps[i].pvAlloc) RTTestIPrintf(RTTESTLVL_ALWAYS, "Warning: Free+Alloc returned different address. new=%p old=%p i=%d\n", pv, s_aOps[i].pvAlloc, i); s_aOps[i].pvAlloc = pv; size_t cbAfterSubAlloc = RTHeapOffsetGetFreeSize(Heap); if (cbBeforeSub != cbAfterSubAlloc) { RTTestIPrintf(RTTESTLVL_ALWAYS, "Warning: cbBeforeSub=%#zx cbAfterSubFree=%#zx cbAfterSubAlloc=%#zx. i=%d\n", cbBeforeSub, cbAfterSubFree, cbAfterSubAlloc, i); //return 1; - won't work correctly until we start creating free block instead of donating memory on alignment. } } /* make a copy of the heap and the to-be-freed list. */ static uint8_t s_abMemCopy[sizeof(s_abMem)]; memcpy(s_abMemCopy, s_abMem, sizeof(s_abMem)); uintptr_t offDelta = (uintptr_t)&s_abMemCopy[0] - (uintptr_t)&s_abMem[0]; RTHEAPOFFSET hHeapCopy = (RTHEAPOFFSET)((uintptr_t)Heap + offDelta); static struct TstHeapOffsetOps s_aOpsCopy[RT_ELEMENTS(s_aOps)]; memcpy(&s_aOpsCopy[0], &s_aOps[0], sizeof(s_aOps)); /* free it in a specific order. */ int cFreed = 0; for (i = 0; i < RT_ELEMENTS(s_aOps); i++) { unsigned j; for (j = 0; j < RT_ELEMENTS(s_aOps); j++) { if ( s_aOps[j].iFreeOrder != i || !s_aOps[j].pvAlloc) continue; //RTPrintf("j=%d i=%d free=%d cb=%d pv=%p\n", j, i, RTHeapOffsetGetFreeSize(Heap), s_aOps[j].cb, s_aOps[j].pvAlloc); RTHeapOffsetFree(Heap, s_aOps[j].pvAlloc); s_aOps[j].pvAlloc = NULL; cFreed++; } } RTTESTI_CHECK(cFreed == RT_ELEMENTS(s_aOps)); RTTestIPrintf(RTTESTLVL_ALWAYS, "i=done free=%d\n", RTHeapOffsetGetFreeSize(Heap)); /* check that we're back at the right amount of free memory. */ size_t cbAfter = RTHeapOffsetGetFreeSize(Heap); if (cbBefore != cbAfter) { RTTestIPrintf(RTTESTLVL_ALWAYS, "Warning: Either we've split out an alignment chunk at the start, or we've got\n" " an alloc/free accounting bug: cbBefore=%d cbAfter=%d\n", cbBefore, cbAfter); RTHeapOffsetDump(Heap, (PFNRTHEAPOFFSETPRINTF)RTPrintf); } /* relocate and free the bits in heap2 now. */ RTTestSub(hTest, "Relocated Heap"); /* free it in a specific order. */ int cFreed2 = 0; for (i = 0; i < RT_ELEMENTS(s_aOpsCopy); i++) { unsigned j; for (j = 0; j < RT_ELEMENTS(s_aOpsCopy); j++) { if ( s_aOpsCopy[j].iFreeOrder != i || !s_aOpsCopy[j].pvAlloc) continue; //RTPrintf("j=%d i=%d free=%d cb=%d pv=%p\n", j, i, RTHeapOffsetGetFreeSize(hHeapCopy), s_aOpsCopy[j].cb, s_aOpsCopy[j].pvAlloc); RTHeapOffsetFree(hHeapCopy, (uint8_t *)s_aOpsCopy[j].pvAlloc + offDelta); s_aOpsCopy[j].pvAlloc = NULL; cFreed2++; } } RTTESTI_CHECK(cFreed2 == RT_ELEMENTS(s_aOpsCopy)); /* check that we're back at the right amount of free memory. */ size_t cbAfterCopy = RTHeapOffsetGetFreeSize(hHeapCopy); RTTESTI_CHECK_MSG(cbAfterCopy == cbAfter, ("cbAfterCopy=%zu cbAfter=%zu\n", cbAfterCopy, cbAfter)); /* * Use random allocation pattern */ RTTestSub(hTest, "Random Test"); RTTESTI_CHECK_RC(rc = RTHeapOffsetInit(&Heap, &s_abMem[1], sizeof(s_abMem) - 1), VINF_SUCCESS); if (RT_FAILURE(rc)) return RTTestSummaryAndDestroy(hTest); RTRAND hRand; RTTESTI_CHECK_RC(rc = RTRandAdvCreateParkMiller(&hRand), VINF_SUCCESS); if (RT_FAILURE(rc)) return RTTestSummaryAndDestroy(hTest); #if 0 RTRandAdvSeed(hRand, 42); #else RTRandAdvSeed(hRand, RTTimeNanoTS()); #endif static struct { size_t cb; void *pv; } s_aHistory[1536]; RT_ZERO(s_aHistory); for (unsigned iTest = 0; iTest < 131072; iTest++) { i = RTRandAdvU32Ex(hRand, 0, RT_ELEMENTS(s_aHistory) - 1); if (!s_aHistory[i].pv) { uint32_t uAlignment = 1 << RTRandAdvU32Ex(hRand, 0, 7); s_aHistory[i].cb = RTRandAdvU32Ex(hRand, 9, 1024); s_aHistory[i].pv = RTHeapOffsetAlloc(Heap, s_aHistory[i].cb, uAlignment); if (!s_aHistory[i].pv) { s_aHistory[i].cb = 9; s_aHistory[i].pv = RTHeapOffsetAlloc(Heap, s_aHistory[i].cb, 0); } if (s_aHistory[i].pv) memset(s_aHistory[i].pv, 0xbb, s_aHistory[i].cb); } else { RTHeapOffsetFree(Heap, s_aHistory[i].pv); s_aHistory[i].pv = NULL; } if ((iTest % 7777) == 7776) { /* exhaust the heap */ for (i = 0; i < RT_ELEMENTS(s_aHistory) && RTHeapOffsetGetFreeSize(Heap) >= 256; i++) if (!s_aHistory[i].pv) { s_aHistory[i].cb = RTRandAdvU32Ex(hRand, 256, 16384); s_aHistory[i].pv = RTHeapOffsetAlloc(Heap, s_aHistory[i].cb, 0); } for (i = 0; i < RT_ELEMENTS(s_aHistory) && RTHeapOffsetGetFreeSize(Heap); i++) { if (!s_aHistory[i].pv) { s_aHistory[i].cb = 1; s_aHistory[i].pv = RTHeapOffsetAlloc(Heap, s_aHistory[i].cb, 1); } if (s_aHistory[i].pv) memset(s_aHistory[i].pv, 0x55, s_aHistory[i].cb); } RTTESTI_CHECK_MSG(RTHeapOffsetGetFreeSize(Heap) == 0, ("%zu\n", RTHeapOffsetGetFreeSize(Heap))); } else if ((iTest % 7777) == 1111) { /* free all */ for (i = 0; i < RT_ELEMENTS(s_aHistory); i++) { RTHeapOffsetFree(Heap, s_aHistory[i].pv); s_aHistory[i].pv = NULL; } size_t cbAfterRand = RTHeapOffsetGetFreeSize(Heap); RTTESTI_CHECK_MSG(cbAfterRand == cbAfter, ("cbAfterRand=%zu cbAfter=%zu\n", cbAfterRand, cbAfter)); } } /* free the rest. */ for (i = 0; i < RT_ELEMENTS(s_aHistory); i++) { RTHeapOffsetFree(Heap, s_aHistory[i].pv); s_aHistory[i].pv = NULL; } /* check that we're back at the right amount of free memory. */ size_t cbAfterRand = RTHeapOffsetGetFreeSize(Heap); RTTESTI_CHECK_MSG(cbAfterRand == cbAfter, ("cbAfterRand=%zu cbAfter=%zu\n", cbAfterRand, cbAfter)); return RTTestSummaryAndDestroy(hTest); }
static DECLCALLBACK(int) Test4Thread(RTTHREAD ThreadSelf, void *pvUser) { /* Use randomization to get a little more variation of the sync pattern. We use a pseudo random generator here so that we don't end up testing the speed of the /dev/urandom implementation, but rather the read-write semaphores. */ int rc; RTRAND hRand; RTTEST_CHECK_RC_OK_RET(g_hTest, rc = RTRandAdvCreateParkMiller(&hRand), rc); RTTEST_CHECK_RC_OK_RET(g_hTest, rc = RTRandAdvSeed(hRand, (uintptr_t)ThreadSelf), rc); unsigned c100 = RTRandAdvU32Ex(hRand, 0, 99); uint64_t *pcItr = (uint64_t *)pvUser; bool fWrite; for (;;) { unsigned readrec = RTRandAdvU32Ex(hRand, 0, 3); unsigned writerec = RTRandAdvU32Ex(hRand, 0, 3); /* Don't overdo recursion testing. */ if (readrec > 1) readrec--; if (writerec > 1) writerec--; fWrite = (c100 < g_uWritePercent); if (fWrite) { for (unsigned i = 0; i <= writerec; i++) { rc = RTCritSectRwEnterExcl(&g_CritSectRw); if (RT_FAILURE(rc)) { RTTestFailed(g_hTest, "Write recursion %u on %s failed with rc=%Rrc", i, RTThreadSelfName(), rc); break; } } if (RT_FAILURE(rc)) break; if (ASMAtomicIncU32(&g_cConcurrentWriters) != 1) { RTTestFailed(g_hTest, "g_cConcurrentWriters=%u on %s after write locking it", g_cConcurrentWriters, RTThreadSelfName()); break; } if (g_cConcurrentReaders != 0) { RTTestFailed(g_hTest, "g_cConcurrentReaders=%u on %s after write locking it", g_cConcurrentReaders, RTThreadSelfName()); break; } } else { rc = RTCritSectRwEnterShared(&g_CritSectRw); if (RT_FAILURE(rc)) { RTTestFailed(g_hTest, "Read locking on %s failed with rc=%Rrc", RTThreadSelfName(), rc); break; } ASMAtomicIncU32(&g_cConcurrentReaders); if (g_cConcurrentWriters != 0) { RTTestFailed(g_hTest, "g_cConcurrentWriters=%u on %s after read locking it", g_cConcurrentWriters, RTThreadSelfName()); break; } } for (unsigned i = 0; i < readrec; i++) { rc = RTCritSectRwEnterShared(&g_CritSectRw); if (RT_FAILURE(rc)) { RTTestFailed(g_hTest, "Read recursion %u on %s failed with rc=%Rrc", i, RTThreadSelfName(), rc); break; } } if (RT_FAILURE(rc)) break; /* * Check for fairness: The values of the threads should not differ too much */ (*pcItr)++; /* * Check for correctness: Give other threads a chance. If the implementation is * correct, no other thread will be able to enter this lock now. */ if (g_fYield) RTThreadYield(); for (unsigned i = 0; i < readrec; i++) { rc = RTCritSectRwLeaveShared(&g_CritSectRw); if (RT_FAILURE(rc)) { RTTestFailed(g_hTest, "Read release %u on %s failed with rc=%Rrc", i, RTThreadSelfName(), rc); break; } } if (RT_FAILURE(rc)) break; if (fWrite) { if (ASMAtomicDecU32(&g_cConcurrentWriters) != 0) { RTTestFailed(g_hTest, "g_cConcurrentWriters=%u on %s before write release", g_cConcurrentWriters, RTThreadSelfName()); break; } if (g_cConcurrentReaders != 0) { RTTestFailed(g_hTest, "g_cConcurrentReaders=%u on %s before write release", g_cConcurrentReaders, RTThreadSelfName()); break; } for (unsigned i = 0; i <= writerec; i++) { rc = RTCritSectRwLeaveExcl(&g_CritSectRw); if (RT_FAILURE(rc)) { RTTestFailed(g_hTest, "Write release %u on %s failed with rc=%Rrc", i, RTThreadSelfName(), rc); break; } } } else { if (g_cConcurrentWriters != 0) { RTTestFailed(g_hTest, "g_cConcurrentWriters=%u on %s before read release", g_cConcurrentWriters, RTThreadSelfName()); break; } ASMAtomicDecU32(&g_cConcurrentReaders); rc = RTCritSectRwLeaveShared(&g_CritSectRw); if (RT_FAILURE(rc)) { RTTestFailed(g_hTest, "Read release on %s failed with rc=%Rrc", RTThreadSelfName(), rc); break; } } if (g_fTerminate) break; c100++; c100 %= 100; } if (!g_fQuiet) RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Thread %s exited with %lld\n", RTThreadSelfName(), *pcItr); RTRandAdvDestroy(hRand); return VINF_SUCCESS; }
static int tstRandAdv(RTRAND hRand) { /* * Test distribution. */ #if 1 /* unsigned 32-bit */ static const struct { uint32_t u32First; uint32_t u32Last; } s_aU32Tests[] = { { 0, UINT32_MAX }, { 0, UINT32_MAX / 2 + UINT32_MAX / 4 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 8 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 16 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 64 }, { 0, UINT32_MAX / 2 }, { UINT32_MAX / 4, UINT32_MAX / 4 * 3 }, { 0, TST_RAND_SAMPLE_RANGES - 1 }, { 1234, 1234 + TST_RAND_SAMPLE_RANGES - 1 }, }; for (unsigned iTest = 0; iTest < RT_ELEMENTS(s_aU32Tests); iTest++) { uint32_t acHits[TST_RAND_SAMPLE_RANGES] = {0}; uint32_t const uFirst = s_aU32Tests[iTest].u32First; uint32_t const uLast = s_aU32Tests[iTest].u32Last; uint32_t const uRange = uLast - uFirst; Assert(uLast >= uFirst); uint32_t const uDivisor = uRange / TST_RAND_SAMPLE_RANGES + 1; RTPrintf("tstRand: TESTING RTRandAdvU32Ex(,%#RX32, %#RX32) distribution... [div=%#RX32 range=%#RX32]\n", uFirst, uLast, uDivisor, uRange); for (unsigned iSample = 0; iSample < TST_RAND_SAMPLE_RANGES * 10240; iSample++) { uint32_t uRand = RTRandAdvU32Ex(hRand, uFirst, uLast); CHECK_EXPR_MSG(uRand >= uFirst, ("%#RX32 %#RX32\n", uRand, uFirst)); CHECK_EXPR_MSG(uRand <= uLast, ("%#RX32 %#RX32\n", uRand, uLast)); uint32_t off = uRand - uFirst; acHits[off / uDivisor]++; } tstRandCheckDist(acHits, iTest); } #endif #if 1 /* unsigned 64-bit */ static const struct { uint64_t u64First; uint64_t u64Last; } s_aU64Tests[] = { { 0, UINT64_MAX }, { 0, UINT64_MAX / 2 + UINT64_MAX / 4 }, { 0, UINT64_MAX / 2 + UINT64_MAX / 8 }, { 0, UINT64_MAX / 2 + UINT64_MAX / 16 }, { 0, UINT64_MAX / 2 + UINT64_MAX / 64 }, { 0, UINT64_MAX / 2 }, { UINT64_MAX / 4, UINT64_MAX / 4 * 3 }, { 0, UINT32_MAX }, { 0, UINT32_MAX / 2 + UINT32_MAX / 4 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 8 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 16 }, { 0, UINT32_MAX / 2 + UINT32_MAX / 64 }, { 0, UINT32_MAX / 2 }, { UINT32_MAX / 4, UINT32_MAX / 4 * 3 }, { 0, TST_RAND_SAMPLE_RANGES - 1 }, { 1234, 1234 + TST_RAND_SAMPLE_RANGES - 1 }, }; for (unsigned iTest = 0; iTest < RT_ELEMENTS(s_aU64Tests); iTest++) { uint32_t acHits[TST_RAND_SAMPLE_RANGES] = {0}; uint64_t const uFirst = s_aU64Tests[iTest].u64First; uint64_t const uLast = s_aU64Tests[iTest].u64Last; uint64_t const uRange = uLast - uFirst; Assert(uLast >= uFirst); uint64_t const uDivisor = uRange / TST_RAND_SAMPLE_RANGES + 1; RTPrintf("tstRand: TESTING RTRandAdvU64Ex(,%#RX64, %#RX64) distribution... [div=%#RX64 range=%#RX64]\n", uFirst, uLast, uDivisor, uRange); for (unsigned iSample = 0; iSample < TST_RAND_SAMPLE_RANGES * 10240; iSample++) { uint64_t uRand = RTRandAdvU64Ex(hRand, uFirst, uLast); CHECK_EXPR_MSG(uRand >= uFirst, ("%#RX64 %#RX64\n", uRand, uFirst)); CHECK_EXPR_MSG(uRand <= uLast, ("%#RX64 %#RX64\n", uRand, uLast)); uint64_t off = uRand - uFirst; acHits[off / uDivisor]++; } tstRandCheckDist(acHits, iTest); } #endif #if 1 /* signed 32-bit */ static const struct { int32_t i32First; int32_t i32Last; } s_aS32Tests[] = { { -429496729, 429496729 }, { INT32_MIN, INT32_MAX }, { INT32_MIN, INT32_MAX / 2 }, { -0x20000000, INT32_MAX }, { -0x10000000, INT32_MAX }, { -0x08000000, INT32_MAX }, { -0x00800000, INT32_MAX }, { -0x00080000, INT32_MAX }, { -0x00008000, INT32_MAX }, { -0x00000800, INT32_MAX }, { 2, INT32_MAX / 2 }, { 4000000, INT32_MAX / 2 }, { -4000000, INT32_MAX / 2 }, { INT32_MIN / 2, INT32_MAX / 2 }, { INT32_MIN / 3, INT32_MAX / 2 }, { INT32_MIN / 3, INT32_MAX / 3 }, { INT32_MIN / 3, INT32_MAX / 4 }, { INT32_MIN / 4, INT32_MAX / 4 }, { INT32_MIN / 5, INT32_MAX / 5 }, { INT32_MIN / 6, INT32_MAX / 6 }, { INT32_MIN / 7, INT32_MAX / 6 }, { INT32_MIN / 7, INT32_MAX / 7 }, { INT32_MIN / 7, INT32_MAX / 8 }, { INT32_MIN / 8, INT32_MAX / 8 }, { INT32_MIN / 9, INT32_MAX / 9 }, { INT32_MIN / 9, INT32_MAX / 12 }, { INT32_MIN / 12, INT32_MAX / 12 }, { 0, TST_RAND_SAMPLE_RANGES - 1 }, { -TST_RAND_SAMPLE_RANGES / 2, TST_RAND_SAMPLE_RANGES / 2 - 1 }, }; for (unsigned iTest = 0; iTest < RT_ELEMENTS(s_aS32Tests); iTest++) { uint32_t acHits[TST_RAND_SAMPLE_RANGES] = {0}; int32_t const iFirst = s_aS32Tests[iTest].i32First; int32_t const iLast = s_aS32Tests[iTest].i32Last; uint32_t const uRange = iLast - iFirst; AssertMsg(iLast >= iFirst, ("%d\n", iTest)); uint32_t const uDivisor = (uRange ? uRange : UINT32_MAX) / TST_RAND_SAMPLE_RANGES + 1; RTPrintf("tstRand: TESTING RTRandAdvS32Ex(,%#RI32, %#RI32) distribution... [div=%#RX32 range=%#RX32]\n", iFirst, iLast, uDivisor, uRange); for (unsigned iSample = 0; iSample < TST_RAND_SAMPLE_RANGES * 10240; iSample++) { int32_t iRand = RTRandAdvS32Ex(hRand, iFirst, iLast); CHECK_EXPR_MSG(iRand >= iFirst, ("%#RI32 %#RI32\n", iRand, iFirst)); CHECK_EXPR_MSG(iRand <= iLast, ("%#RI32 %#RI32\n", iRand, iLast)); uint32_t off = iRand - iFirst; acHits[off / uDivisor]++; } tstRandCheckDist(acHits, iTest); } #endif #if 1 /* signed 64-bit */ static const struct { int64_t i64First; int64_t i64Last; } s_aS64Tests[] = { { INT64_MIN, INT64_MAX }, { INT64_MIN, INT64_MAX / 2 }, { INT64_MIN / 2, INT64_MAX / 2 }, { INT64_MIN / 2 + INT64_MIN / 4, INT64_MAX / 2 }, { INT64_MIN / 2 + INT64_MIN / 8, INT64_MAX / 2 }, { INT64_MIN / 2 + INT64_MIN / 16, INT64_MAX / 2 }, { INT64_MIN / 2 + INT64_MIN / 64, INT64_MAX / 2 }, { INT64_MIN / 2 + INT64_MIN / 64, INT64_MAX / 2 + INT64_MAX / 64 }, { INT64_MIN / 2, INT64_MAX / 2 + INT64_MAX / 64 }, { INT64_MIN / 2, INT64_MAX / 2 + INT64_MAX / 8 }, { INT64_MIN / 2, INT64_MAX / 2 - INT64_MAX / 8 }, { INT64_MIN / 2 - INT64_MIN / 4, INT64_MAX / 2 - INT64_MAX / 4 }, { INT64_MIN / 2 - INT64_MIN / 4, INT64_MAX / 2 - INT64_MAX / 8 }, { INT64_MIN / 2 - INT64_MIN / 8, INT64_MAX / 2 - INT64_MAX / 8 }, { INT64_MIN / 2 - INT64_MIN / 16, INT64_MAX / 2 - INT64_MAX / 8 }, { INT64_MIN / 2 - INT64_MIN / 16, INT64_MAX / 2 - INT64_MAX / 16 }, { INT64_MIN / 2 - INT64_MIN / 32, INT64_MAX / 2 - INT64_MAX / 16 }, { INT64_MIN / 2 - INT64_MIN / 32, INT64_MAX / 2 - INT64_MAX / 32 }, { INT64_MIN / 2 - INT64_MIN / 64, INT64_MAX / 2 - INT64_MAX / 64 }, { INT64_MIN / 2 - INT64_MIN / 8, INT64_MAX / 2 }, { INT64_MIN / 4, INT64_MAX / 4 }, { INT64_MIN / 5, INT64_MAX / 5 }, { INT64_MIN / 6, INT64_MAX / 6 }, { INT64_MIN / 7, INT64_MAX / 7 }, { INT64_MIN / 8, INT64_MAX / 8 }, { INT32_MIN, INT32_MAX }, { INT32_MIN, INT32_MAX / 2 }, { -0x20000000, INT32_MAX }, { -0x10000000, INT32_MAX }, { -0x7f000000, INT32_MAX }, { -0x08000000, INT32_MAX }, { -0x00800000, INT32_MAX }, { -0x00080000, INT32_MAX }, { -0x00008000, INT32_MAX }, { 2, INT32_MAX / 2 }, { 4000000, INT32_MAX / 2 }, { -4000000, INT32_MAX / 2 }, { INT32_MIN / 2, INT32_MAX / 2 }, { 0, TST_RAND_SAMPLE_RANGES - 1 }, { -TST_RAND_SAMPLE_RANGES / 2, TST_RAND_SAMPLE_RANGES / 2 - 1 } }; for (unsigned iTest = 0; iTest < RT_ELEMENTS(s_aS64Tests); iTest++) { uint32_t acHits[TST_RAND_SAMPLE_RANGES] = {0}; int64_t const iFirst = s_aS64Tests[iTest].i64First; int64_t const iLast = s_aS64Tests[iTest].i64Last; uint64_t const uRange = iLast - iFirst; AssertMsg(iLast >= iFirst, ("%d\n", iTest)); uint64_t const uDivisor = (uRange ? uRange : UINT64_MAX) / TST_RAND_SAMPLE_RANGES + 1; RTPrintf("tstRand: TESTING RTRandAdvS64Ex(,%#RI64, %#RI64) distribution... [div=%#RX64 range=%#016RX64]\n", iFirst, iLast, uDivisor, uRange); for (unsigned iSample = 0; iSample < TST_RAND_SAMPLE_RANGES * 10240; iSample++) { int64_t iRand = RTRandAdvS64Ex(hRand, iFirst, iLast); CHECK_EXPR_MSG(iRand >= iFirst, ("%#RI64 %#RI64\n", iRand, iFirst)); CHECK_EXPR_MSG(iRand <= iLast, ("%#RI64 %#RI64\n", iRand, iLast)); uint64_t off = iRand - iFirst; acHits[off / uDivisor]++; } tstRandCheckDist(acHits, iTest); } #endif /* * Test saving and restoring the state. */ RTPrintf("tstRand: TESTING RTRandAdvSave/RestoreSave\n"); char szState[256]; size_t cbState = sizeof(szState); int rc = RTRandAdvSaveState(hRand, szState, &cbState); if (rc != VERR_NOT_SUPPORTED) { CHECK_EXPR_MSG(rc == VINF_SUCCESS, ("RTRandAdvSaveState(%p,,256) -> %Rrc (%d)\n", (uintptr_t)hRand, rc, rc)); uint32_t const u32A1 = RTRandAdvU32(hRand); uint32_t const u32B1 = RTRandAdvU32(hRand); RTPrintf("tstRand: state:\"%s\" A=%RX32 B=%RX32\n", szState, u32A1, u32B1); rc = RTRandAdvRestoreState(hRand, szState); CHECK_EXPR_MSG(rc == VINF_SUCCESS, ("RTRandAdvRestoreState(%p,\"%s\") -> %Rrc (%d)\n", (uintptr_t)hRand, szState, rc, rc)); uint32_t const u32A2 = RTRandAdvU32(hRand); uint32_t const u32B2 = RTRandAdvU32(hRand); CHECK_EXPR_MSG(u32A1 == u32A2, ("u32A1=%RX32 u32A2=%RX32\n", u32A1, u32A2)); CHECK_EXPR_MSG(u32B1 == u32B2, ("u32B1=%RX32 u32B2=%RX32\n", u32B1, u32B2)); } else { szState[0] = '\0'; rc = RTRandAdvRestoreState(hRand, szState); CHECK_EXPR_MSG(rc == VERR_NOT_SUPPORTED, ("RTRandAdvRestoreState(%p,\"\") -> %Rrc (%d)\n", (uintptr_t)hRand, rc, rc)); } /* * Destroy it. */ rc = RTRandAdvDestroy(hRand); CHECK_EXPR_MSG(rc == VINF_SUCCESS, ("RTRandAdvDestroy(%p) -> %Rrc (%d)\n", (uintptr_t)hRand, rc, rc)); return 0; }
static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest) { int rc; PVBOXHDD pVD = NULL; VDGEOMETRY PCHS = { 0, 0, 0 }; VDGEOMETRY LCHS = { 0, 0, 0 }; PVDINTERFACE pVDIfs = NULL; VDINTERFACEERROR VDIfError; /** Buffer storing the random test pattern. */ uint8_t *pbTestPattern = NULL; /** Number of disk segments */ uint32_t cDiskSegments; /** Array of disk segments */ PVDDISKSEG paDiskSeg = NULL; unsigned cDiffs = 0; unsigned idDiff = 0; /* Diff ID counter for the filename */ /* Delete all images from a previous run. */ RTFileDelete(pTest->pcszBaseImage); for (unsigned i = 0; i < pTest->cIterations; i++) { char *pszDiffFilename = NULL; rc = RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", i, pTest->pcszDiffSuff); if (RT_SUCCESS(rc)) { if (RTFileExists(pszDiffFilename)) RTFileDelete(pszDiffFilename); RTStrFree(pszDiffFilename); } } /* Create the virtual disk test data */ pbTestPattern = (uint8_t *)RTMemAlloc(pTest->cbTestPattern); RTRandAdvBytes(g_hRand, pbTestPattern, pTest->cbTestPattern); cDiskSegments = RTRandAdvU32Ex(g_hRand, pTest->cDiskSegsMin, pTest->cDiskSegsMax); uint64_t cbDisk = 0; paDiskSeg = (PVDDISKSEG)RTMemAllocZ(cDiskSegments * sizeof(VDDISKSEG)); if (!paDiskSeg) { RTPrintf("Failed to allocate memory for random disk segments\n"); g_cErrors++; return VERR_NO_MEMORY; } for (unsigned i = 0; i < cDiskSegments; i++) { paDiskSeg[i].off = cbDisk; paDiskSeg[i].cbSeg = RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 512, pTest->cbTestPattern), 512); if (tstVDSnapIsTrue(pTest->uAllocatedBlocks)) paDiskSeg[i].pbData = pbTestPattern + RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 0, pTest->cbTestPattern - paDiskSeg[i].cbSeg - 512), 512); else paDiskSeg[i].pbData = NULL; /* Not allocated initially */ cbDisk += paDiskSeg[i].cbSeg; } RTPrintf("Disk size is %llu bytes\n", cbDisk); #define CHECK(str) \ do \ { \ RTPrintf("%s rc=%Rrc\n", str, rc); \ if (RT_FAILURE(rc)) \ { \ if (pbTestPattern) \ RTMemFree(pbTestPattern); \ if (paDiskSeg) \ RTMemFree(paDiskSeg); \ VDDestroy(pVD); \ g_cErrors++; \ return rc; \ } \ } while (0) #define CHECK_BREAK(str) \ do \ { \ RTPrintf("%s rc=%Rrc\n", str, rc); \ if (RT_FAILURE(rc)) \ { \ g_cErrors++; \ break; \ } \ } while (0) /* Create error interface. */ /* Create error interface. */ VDIfError.pfnError = tstVDError; VDIfError.pfnMessage = tstVDMessage; rc = VDInterfaceAdd(&VDIfError.Core, "tstVD_Error", VDINTERFACETYPE_ERROR, NULL, sizeof(VDINTERFACEERROR), &pVDIfs); AssertRC(rc); rc = VDCreate(pVDIfs, VDTYPE_HDD, &pVD); CHECK("VDCreate()"); rc = VDCreateBase(pVD, pTest->pcszBackend, pTest->pcszBaseImage, cbDisk, VD_IMAGE_FLAGS_NONE, "Test image", &PCHS, &LCHS, NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL); CHECK("VDCreateBase()"); bool fInit = true; uint32_t cIteration = 0; /* Do the real work now */ while ( RT_SUCCESS(rc) && cIteration < pTest->cIterations) { /* Write */ rc = tstVDSnapWrite(pVD, paDiskSeg, cDiskSegments, cbDisk, fInit); CHECK_BREAK("tstVDSnapWrite()"); fInit = false; /* Write returned, do we want to create a new diff or merge them? */ bool fCreate = cDiffs < pTest->cDiffsMinBeforeMerge ? true : tstVDSnapIsTrue(pTest->uCreateDiffChance); if (fCreate) { char *pszDiffFilename = NULL; RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", idDiff, pTest->pcszDiffSuff); CHECK("RTStrAPrintf()"); idDiff++; cDiffs++; rc = VDCreateDiff(pVD, pTest->pcszBackend, pszDiffFilename, VD_IMAGE_FLAGS_NONE, "Test diff image", NULL, NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL); CHECK_BREAK("VDCreateDiff()"); RTStrFree(pszDiffFilename); VDDumpImages(pVD); /* Change data */ tstVDSnapSegmentsDice(pTest, paDiskSeg, cDiskSegments, pbTestPattern, pTest->cbTestPattern); } else { uint32_t uStartMerge = RTRandAdvU32Ex(g_hRand, 1, cDiffs - 1); uint32_t uEndMerge = RTRandAdvU32Ex(g_hRand, uStartMerge + 1, cDiffs); RTPrintf("Merging %u diffs from %u to %u...\n", uEndMerge - uStartMerge, uStartMerge, uEndMerge); if (pTest->fForward) rc = VDMerge(pVD, uStartMerge, uEndMerge, NULL); else rc = VDMerge(pVD, uEndMerge, uStartMerge, NULL); CHECK_BREAK("VDMerge()"); cDiffs -= uEndMerge - uStartMerge; VDDumpImages(pVD); /* Go through the disk segments and reset pointers. */ for (uint32_t i = 0; i < cDiskSegments; i++) { if (paDiskSeg[i].pbDataDiff) { paDiskSeg[i].pbData = paDiskSeg[i].pbDataDiff; paDiskSeg[i].pbDataDiff = NULL; } } /* Now compare the result with our test pattern */ rc = tstVDSnapReadVerify(pVD, paDiskSeg, cDiskSegments, cbDisk); CHECK_BREAK("tstVDSnapReadVerify()"); } cIteration++; } VDDumpImages(pVD); VDDestroy(pVD); if (paDiskSeg) RTMemFree(paDiskSeg); if (pbTestPattern) RTMemFree(pbTestPattern); RTFileDelete(pTest->pcszBaseImage); for (unsigned i = 0; i < idDiff; i++) { char *pszDiffFilename = NULL; RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", i, pTest->pcszDiffSuff); RTFileDelete(pszDiffFilename); RTStrFree(pszDiffFilename); } #undef CHECK return rc; }
/** * Returns true with the given chance in percent. * * @returns true or false * @param iPercentage The percentage of the chance to return true. */ static bool tstVDSnapIsTrue(int iPercentage) { int uRnd = RTRandAdvU32Ex(g_hRand, 0, 100); return (uRnd <= iPercentage); /* This should be enough for our purpose */ }