Пример #1
0
/**
 * 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);
}
Пример #2
0
/**
 * 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);
}
Пример #3
0
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;
}
Пример #5
0
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 */
}