From 4b0bd74e806fc217c732724b83d638a302ee9aa0 Mon Sep 17 00:00:00 2001 From: KazushiM <85604869+KazushiMe@users.noreply.github.com> Date: Sun, 26 Dec 2021 04:37:18 +0800 Subject: [PATCH] [MemTesterNX] Add multi-thread(3) support and option to stress DRAM --- README.md | 15 +- SdOut/switch/MemTesterNX.nro | Bin 293061 -> 293061 bytes Source/MemTesterNX/source/main.c | 317 +++++++++++++++++++++++------- Source/MemTesterNX/source/tests.c | 227 +++++++++++---------- Source/MemTesterNX/source/tests.h | 3 + 5 files changed, 377 insertions(+), 185 deletions(-) diff --git a/README.md b/README.md index bad189fd..ff536e4c 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ This project will not be actively maintained by me and I'm looking for collabora - Game recording and SysDVR streaming @ 60fps with high video bitrate - Option to change the threshold for chargers providing enough power - **TinyMemBenchNX**: DRAM throughput and latency test based on [tinymembench](https://github.com/ssvb/tinymembench) -- MemTesterNX: A userspace utility for testing DRAM faults based on [memtester](https://pyropus.ca/software/memtester/) - - For testing stability, GPU/DRAM-heavy games like BotW/MHR will do better jobs as it's easier to spot framebuffer corruption/freeze +- **MemTesterNX**: A userspace utility for testing DRAM faults and stability based on [memtester](https://pyropus.ca/software/memtester/) + - Now with multi-thread support and "stress DRAM" option, it should be able to test DRAM stability with adjusted timings. #### Details @@ -104,21 +104,16 @@ This project will not be actively maintained by me and I'm looking for collabora - Modded `loader.kip` with embedded pcv, ptm, am-no-copyright, ValidateAcidSignature patches - Prebuilt sys-clk-OC and ReverseNX-RT modified for OC -- Hekate with DRAM overvolting patch - `system-settings.ini` with some QoL improvements -1. **Restoring pcv backup if you have patched pcv module manually:** Launch the `patcher.te` script via TegraExplorer to restore your backup. Separated **ptm patches should be removed** to avoid conflicts. +1. Copy all the files in `SdOut` to the root of SD card. `system_settings.ini` should be edited manually. -2. Copy all the files in `SdOut` to the root of SD card. `system_settings.ini` should be edited manually. - -3. Grab `x.x.x_loader_xxxx.x.kip` for your Atmosphere version and desired RAM frequency, rename it to `loader.kip` and place it in `/atmosphere/kips/`. - -4. **Hekate-ipl bootloader:** +2. Grab `x.x.x_loader_xxxx.x.kip` for your Atmosphere version and desired RAM frequency, rename it to `loader.kip` and place it in `/atmosphere/kips/`. +3. **Hekate-ipl bootloader** - Rename the kip to `loader.kip` and add `kip1=atmosphere/kips/loader.kip` in `bootloader/hekate_ipl.ini` **Atmosphere Fusee bootloader:** - - Fusee will load any kips in `/atmosphere/kips/` automatically. diff --git a/SdOut/switch/MemTesterNX.nro b/SdOut/switch/MemTesterNX.nro index a43efbd69d68fca26e228feb41c78a04d0faee6c..c6b5587176976cbce64bba6fdca3539ab34772cf 100644 GIT binary patch delta 50758 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 ~HyznA|qjA4xC#tHprQXNb4%n8xoH($qfqzwgvE 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 delta 48687 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 diff --git a/Source/MemTesterNX/source/main.c b/Source/MemTesterNX/source/main.c index c7267b5c..43308259 100644 --- a/Source/MemTesterNX/source/main.c +++ b/Source/MemTesterNX/source/main.c @@ -1,5 +1,6 @@ /* - * memtester version 4 + * MemTesterNX + * based on memtester version 4 * * Very simple but very effective user-space memory tester. * Originally by Simon Kirby @@ -12,7 +13,7 @@ * */ -#define __version__ "4.5.1-full" +#define __version__ "4.5.1-multithread" // Include the most common headers from the C standard library #include @@ -22,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +35,7 @@ #include PadState pad; -unsigned short dividend; +unsigned short dividend = 1; void waitForAnyKey() { while (appletMainLoop()) @@ -73,6 +75,13 @@ void ShowErr(const char* err, const char* details, Result rc) { appletHolderRequestExit(¤tApplet); } +double gettime(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)((int64_t)tv.tv_sec * 1000000 + tv.tv_usec) / 1000000.; +} + struct test tests[] = { { "Random Value", test_random_value }, { "Compare XOR", test_xor_comparison }, @@ -86,7 +95,7 @@ struct test tests[] = { { "Block Sequential", test_blockseq_comparison }, { "Checkerboard", test_checkerboard_comparison }, { "Bit Spread", test_bitspread_comparison }, - { "Bit Flip", test_bitflip_comparison }, + { "Bit Flip (Slow)", test_bitflip_comparison }, { "Walking Ones", test_walkbits1_comparison }, { "Walking Zeroes", test_walkbits0_comparison }, #ifdef TEST_NARROW_WRITES @@ -96,6 +105,13 @@ struct test tests[] = { { NULL, NULL } }; +struct test stress_tests[] = { + { "Stress memcpy x128", test_stress_memcpy }, + { "Stress memset x128", test_stress_memset }, + { "Stress memcmp x 32", test_stress_memcmp }, + { NULL, NULL } +}; + int memtester_pagesize(void) { printf("using pagesize of 4096\n"); return 4096; @@ -104,6 +120,69 @@ int memtester_pagesize(void) { /* Global vars - so tests have access to this information */ int use_phys = 0; off_t physaddrbase = 0; +int testJobId[4] = {0}; +int testWorkerReport[4] = {0}; +size_t bufsize[4], wantbytes[4]; +void volatile *aligned[4]; +Thread threads[4]; +struct test* test_select = tests; + +void testWorker(int div) +{ + // Get ready + while (testJobId[div] != -2) + svcSleepThread(10000000ULL); + + while (true) + { + int currentJobId = -1; + + // Stuck address + while (testJobId[div] != currentJobId) + svcSleepThread(10000000ULL); + + if (!test_stuck_address(aligned[div], bufsize[div]/sizeof(ul))) + { + testWorkerReport[div] = 1; + currentJobId++; + } + else + { + testWorkerReport[div] = -1; + return; + } + + // tests[], currentJobId = 0 here + while (test_select[currentJobId].name) + { + while (testJobId[div] != currentJobId) + svcSleepThread(10000000ULL); + + if (!test_select[currentJobId].fp(aligned[div], ((size_t)aligned[div] + bufsize[div]/2), bufsize[div]/sizeof(ul)/2)) + { + testWorkerReport[div] = 1; + currentJobId++; + } + else + { + testWorkerReport[div] = -1; + return; + } + } + } +} + +void testWorker0(void*) { testWorker(0); } +void testWorker1(void*) { testWorker(1); } +void testWorker2(void*) { testWorker(2); } +void testWorker3(void*) { testWorker(3); } + +void* workers[] = { + testWorker0, + testWorker1, + testWorker2, + testWorker3, +}; // Main program entrypoint int main(int argc, char* argv[]) @@ -121,22 +200,26 @@ int main(int argc, char* argv[]) padInitializeDefault(&pad); - ul loop, i; - unsigned short div, maxdiv = 0; - size_t pagesize, wantraw, wantbytes[4], wantbytes_orig, bufsize[4], halflen, count; + ull loop, i; + unsigned short div, testThreads = 3; + size_t pagesize, wantraw, wantbytes_orig; ptrdiff_t pagesizemask; - void volatile *buf[4], *aligned[4]; - ulv *bufa, *bufb; + void volatile *buf[4]; int memshift; - ul testmask = 0; ull totalmem = 0; + int numOfMallocs = 0; + bool isDevKit8GB = false; - printf("MemTesterNX version " __version__ " (%d-bit)\n", UL_LEN); - printf("Based on memtester. Copyright (C) 2001-2020 Charles Cazabon.\n"); - printf("Licensed under the GNU General Public License version 2 (only).\n\n"); - printf("4.5.1-full supports full RAM test (up to 8GB for devkit).\n"); - printf("It will looping forever until an error shows up or you manually exit to HOME screen.\n\n"); - printf("Press A: long test\nPress B: fast test\nPress any other key: exit\n\n"); + printf("MemTesterNX version " __version__ " (%d-bit)\n"\ + "Based on memtester. Copyright (C) 2001-2020 Charles Cazabon, 2021 KazushiMe.\n"\ + "Licensed under the GNU General Public License version 2 (only).\n\n"\ + "Support full RAM test (up to 8GB) with 3-4 threads.\n"\ + "It will be looping forever until error occurs or user exits to HOME screen.\n\n"\ + "Press A: long test\n"\ + "Press X: fast test\n"\ + "Press Y: stress DRAM (memcpy, memset and memcmp)\n"\ + "Press any other key: exit\n\n", + UL_LEN); while (appletMainLoop()) { @@ -146,14 +229,19 @@ int main(int argc, char* argv[]) if (kDown & HidNpadButton_A) { - dividend = 1; break; } - else if (kDown & HidNpadButton_B) + else if (kDown & HidNpadButton_X) { dividend = 4; break; } + else if (kDown & HidNpadButton_Y) + { + dividend = 16; + test_select = stress_tests; + break; + } else if (kDown) { consoleExit(NULL); @@ -163,20 +251,23 @@ int main(int argc, char* argv[]) consoleUpdate(NULL); } + // Disable auto sleep and request CPU Boost mode + appletSetAutoSleepDisabled(true); + appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad); + pagesize = memtester_pagesize(); pagesizemask = (ptrdiff_t) ~(pagesize - 1); printf("pagesizemask is 0x%tx\n", pagesizemask); consoleUpdate(NULL); memshift = 20; /* megabytes */ - wantraw = 2048; + wantraw = 2048; // HOS limit wantbytes_orig = ((size_t) wantraw << memshift); // Allocate as much RAM as possible. - for(div = 0; div <= 3; div++) + for (div = 0; div <= 3; div++) { - buf[div] = NULL; wantbytes[div] = wantbytes_orig; @@ -186,9 +277,69 @@ int main(int argc, char* argv[]) wantbytes[div] -= pagesize; } + totalmem += wantbytes[div]; + + if ((wantbytes[div] >> memshift) < 2047) + { + numOfMallocs = div + 1; + break; + } + } + + for (div = 0; div < numOfMallocs; div++) + { + free((void *)buf[div]); + } + + // Unknown: Not sure if devkit could use full 8GB in application space + if ((totalmem >> memshift) > 3 * 2047) + { + isDevKit8GB = true; + testThreads = 4; + } + + // Create workers + for (div = 0; div < testThreads; div++) + { + Result rc; + rc = threadCreate(&threads[div], workers[div], NULL, NULL, 0x1000, 0x2C, div == 3 ? -2 : div); + if (R_FAILED(rc)) + { + printf("Fatal: threadCreate[%d] failed: 0x%X\n", div, rc); + consoleUpdate(NULL); + } + else + { + totalmem -= 0x1000; + } + } + + printf("\nTotal RAM available: %lldMB\n\n", totalmem >> memshift); + consoleUpdate(NULL); + + // Redistribute RAM allocation equally to testThreads workers + for (div = 0; div < testThreads; div++) + { + buf[div] = NULL; + if (isDevKit8GB) + { + if (div != 3) + wantbytes[div] = totalmem / 3; + else + wantbytes[div] = 2047; + } + else + { + wantbytes[div] = totalmem / testThreads; + } + + while (!buf[div] && wantbytes[div]) { + buf[div] = (void volatile *) malloc(wantbytes[div]); + if (!buf[div]) + wantbytes[div] -= pagesize; + } + bufsize[div] = wantbytes[div]; - printf("Alloc %d: got %lluMB (%llu bytes)\n", div+1, (ull) wantbytes[div] >> 20, (ull) wantbytes[div]); - consoleUpdate(NULL); /* Do alighnment here as well, as some cases won't trigger above if you define out the use of mlock() (cough HP/UX 10 cough). */ @@ -203,68 +354,92 @@ int main(int argc, char* argv[]) aligned[div] = buf[div]; } - totalmem += wantbytes[div]; + printf("Alloc %d: got %lluMB (%llu bytes)\n", div+1, (ull) wantbytes[div] >> memshift, (ull) wantbytes[div]); + consoleUpdate(NULL); + } - if((wantbytes[div] >> 20) < 2047) + // Start workers + for (div = 0; div < testThreads; div++) + { + Result rc; + rc = threadStart(&threads[div]); + if (R_FAILED(rc)) { - maxdiv = div; - break; + printf("Fatal: threadStart[%d] failed: 0x%X\n", div, rc); + consoleUpdate(NULL); } } - printf("\nTotal RAM allocated: %lldMB\n\n", totalmem >> 20); - consoleUpdate(NULL); - for(loop=1;;loop++) { - printf("Loop %lu:\n", loop); + // Set testJobId to -2(Ready) + for (int j = 0; j < testThreads; j++) + testJobId[j] = -2; - for(div=0; div<=maxdiv; div++) + printf("Loop %llu:\n", loop); + + // Stuck address (-1) + printf(" %-20s: ", "Stuck Address"); + consoleUpdate(NULL); + for (int j = 0; j < testThreads; j++) + testJobId[j] = -1; + + ull count = 0; + for (int j = 0; j < testThreads; ) { - printf("Alloc %d:\n", div+1); - printf(" %-20s: ", "Stuck Address"); - consoleUpdate(NULL); - - halflen = bufsize[div] / 2; - count = halflen / sizeof(ul); - bufa = (ulv *) aligned[div]; - bufb = (ulv *) ((size_t) aligned[div] + halflen); - - if (!test_stuck_address(aligned[div], bufsize[div] / sizeof(ul))) { - printf("ok\n"); - } - else + count++; + switch (testWorkerReport[j]) { - printf("Alloc %d: Error!\nPress any key to exit.\n", div+1); - waitForAnyKey(); - consoleExit(NULL); - return 0; - } - consoleUpdate(NULL); - for (i=0;;i++) { - if (!tests[i].name) break; - /* If using a custom testmask, only run this test if the - bit corresponding to this test was set by the user. - */ - if (testmask && (!((1 << i) & testmask))) { - continue; - } - printf(" %-20s: ", tests[i].name); - if (!tests[i].fp(bufa, bufb, count)) { - printf("ok\n"); - } - else - { - printf("Alloc %d: Error!\nPress any key to exit.\n", div+1); + case 0: + svcSleepThread(10000000ULL); + break; + case -1: + printf("Alloc %d/%d: Error detected!\nPress any key to exit.\n", j+1, testThreads+1); + consoleUpdate(NULL); waitForAnyKey(); consoleExit(NULL); return 0; - } - consoleUpdate(NULL); - /* clear buffer */ - memset((void *) buf[div], 255, wantbytes[div]); + case 1: + testWorkerReport[j] = 0; + j++; + continue; } - printf("\n"); + } + printf("ok\n"); + consoleUpdate(NULL); + + // tests[] + for (i = 0 ;; i++) { + if (!test_select[i].name) + break; + + printf(" %-20s: ...", test_select[i].name); + consoleUpdate(NULL); + for (int j = 0; j < testThreads; j++) + testJobId[j] = i; + + double start_sec = gettime(); + for (int j = 0; j < testThreads; ) + { + switch (testWorkerReport[j]) + { + case 0: + svcSleepThread(10000000ULL); + continue; + case -1: + printf("Alloc %d/%d: Error detected!\nPress any key to exit.\n", j+1, testThreads+1); + consoleUpdate(NULL); + waitForAnyKey(); + consoleExit(NULL); + return 0; + case 1: + testWorkerReport[j] = 0; + j++; + continue; + } + } + double end_sec = gettime(); + printf("\b\b\bok! finished in %.1fs\n", end_sec - start_sec); consoleUpdate(NULL); } } diff --git a/Source/MemTesterNX/source/tests.c b/Source/MemTesterNX/source/tests.c index 30a5cf19..7b5f07a3 100755 --- a/Source/MemTesterNX/source/tests.c +++ b/Source/MemTesterNX/source/tests.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "tests.h" #include "types.h" @@ -48,18 +49,17 @@ int compare_regions(ulv *bufa, ulv *bufb, size_t count) { size_t i; ulv *p1 = bufa; ulv *p2 = bufb; - off_t physaddr; + // off_t physaddr; for (i = 0; i < count; i++, p1++, p2++) { if (*p1 != *p2) { - if (use_phys) { - physaddr = physaddrbase + (i * sizeof(ul)); - printf("FAILURE: 0x%08lx != 0x%08lx at physical address 0x%08lx.\n", (ul) *p1, (ul) *p2, physaddr); - } else { - printf("FAILURE: 0x%08lx != 0x%08lx at offset 0x%08lx.\n", (ul) *p1, (ul) *p2, (ul) (i * sizeof(ul))); - } - consoleUpdate(NULL); - /* printf("Skipping to next test..."); */ + // if (use_phys) { + // physaddr = physaddrbase + (i * sizeof(ul)); + // printf("FAILURE: 0x%08lx != 0x%08lx at physical address 0x%08lx.\n", (ul) *p1, (ul) *p2, physaddr); + // } else { + // printf("FAILURE: 0x%08lx != 0x%08lx at offset 0x%08lx.\n", (ul) *p1, (ul) *p2, (ul) (i * sizeof(ul))); + // } + /* // printf("Skipping to next test..."); */ r = -1; } } @@ -70,57 +70,97 @@ int test_stuck_address(ulv *bufa, size_t count) { ulv *p1 = bufa; unsigned int j; size_t i; - off_t physaddr; + // off_t physaddr; - printf(" "); + // printf(" "); for (j = 0; j < (16 / dividend) ; j++) { - printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); p1 = (ulv *) bufa; - printf("setting %3u", j); - consoleUpdate(NULL); + // printf("setting %3u", j); for (i = 0; i < count; i++) { *p1 = ((j + i) % 2) == 0 ? (ul) p1 : ~((ul) p1); *p1++; } - printf("\b\b\b\b\b\b\b\b\b\b\b"); - printf("testing %3u", j); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("testing %3u", j); p1 = (ulv *) bufa; for (i = 0; i < count; i++, p1++) { if (*p1 != (((j + i) % 2) == 0 ? (ul) p1 : ~((ul) p1))) { - if (use_phys) { - physaddr = physaddrbase + (i * sizeof(ul)); - printf("FAILURE: possible bad address line at physical address 0x%08lx.\n", physaddr); - } else { - printf("FAILURE: possible bad address line at offset 0x%08lx.\n", (ul) (i * sizeof(ul))); - } - printf("Skipping to next test...\n"); - consoleUpdate(NULL); + // if (use_phys) { + // physaddr = physaddrbase + (i * sizeof(ul)); + // printf("FAILURE: possible bad address line at physical address 0x%08lx.\n", physaddr); + // } else { + // printf("FAILURE: possible bad address line at offset 0x%08lx.\n", (ul) (i * sizeof(ul))); + // } + // printf("Skipping to next test...\n"); return -1; } } } - printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); return 0; } +int test_stress_memcpy(ulv *bufa, ulv *bufb, size_t count) { + unsigned int j; + + int q = rand_ul(); + memset((void*)bufa, q, count*sizeof(ul)); + memset((void*)bufb, q, count*sizeof(ul)); + + for (j = 0; j < 128; j++) { + memcpy((void*)bufa, (void*)bufb, count*sizeof(ul)); + } + return memcmp((void*)bufa, (void*)bufb, count*sizeof(ul)); +} + +int test_stress_memset(ulv *bufa, ulv *bufb, size_t count) { + unsigned int j; + + for (j = 0; j < 128; j++) { + int q = rand_ul(); + memset((void*)bufa, q, count*sizeof(ul)); + memset((void*)bufb, q, count*sizeof(ul)); + } + return memcmp((void*)bufa, (void*)bufb, count*sizeof(ul)); +} + +int test_stress_memcmp(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + unsigned int j; + + int q = rand_ul(); + memset((void*)bufa, q, count*sizeof(ul)); + memset((void*)bufb, q, count*sizeof(ul)); + + int rc = 0; + + for (j = 0; j < 32; j++) { + // Trick compiler so that it won't skip memcmp + *p1++ = *p2++ = rand_ul(); + rc = memcmp((void*)bufa, (void*)bufb, count*sizeof(ul)); + if (rc) + return rc; + } + return rc; +} + int test_random_value(ulv *bufa, ulv *bufb, size_t count) { ulv *p1 = bufa; ulv *p2 = bufb; - ul j = 0; + // ul j = 0; size_t i; - putchar(' '); + // putchar(' '); for (i = 0; i < count; i++) { *p1++ = *p2++ = rand_ul(); if (!(i % PROGRESSOFTEN)) { - putchar('\b'); - putchar(progress[++j % PROGRESSLEN]); - consoleUpdate(NULL); + // putchar('\b'); + // putchar(progress[++j % PROGRESSLEN]); } } - printf("\b \b"); - consoleUpdate(NULL); + // printf("\b \b"); return compare_regions(bufa, bufb, count); } @@ -224,26 +264,23 @@ int test_solidbits_comparison(ulv *bufa, ulv *bufb, size_t count) { ul q; size_t i; - printf(" "); + // printf(" "); for (j = 0; j < (64 / dividend / dividend); j++) { - printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); q = (j % 2) == 0 ? UL_ONEBITS : 0; - printf("setting %3u", j); - consoleUpdate(NULL); + // printf("setting %3u", j); p1 = (ulv *) bufa; p2 = (ulv *) bufb; for (i = 0; i < count; i++) { *p1++ = *p2++ = (i % 2) == 0 ? q : ~q; } - printf("\b\b\b\b\b\b\b\b\b\b\b"); - printf("testing %3u", j); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("testing %3u", j); if (compare_regions(bufa, bufb, count)) { return -1; } } - printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); return 0; } @@ -254,26 +291,23 @@ int test_checkerboard_comparison(ulv *bufa, ulv *bufb, size_t count) { ul q; size_t i; - printf(" "); + // printf(" "); for (j = 0; j < (64 / dividend / dividend); j++) { - printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); q = (j % 2) == 0 ? CHECKERBOARD1 : CHECKERBOARD2; - printf("setting %3u", j); - consoleUpdate(NULL); + // printf("setting %3u", j); p1 = (ulv *) bufa; p2 = (ulv *) bufb; for (i = 0; i < count; i++) { *p1++ = *p2++ = (i % 2) == 0 ? q : ~q; } - printf("\b\b\b\b\b\b\b\b\b\b\b"); - printf("testing %3u", j); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("testing %3u", j); if (compare_regions(bufa, bufb, count)) { return -1; } } - printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); return 0; } @@ -283,25 +317,22 @@ int test_blockseq_comparison(ulv *bufa, ulv *bufb, size_t count) { unsigned int j; size_t i; - printf(" "); + // printf(" "); for (j = 0; j < (64 / dividend / dividend) ; j++) { - printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); p1 = (ulv *) bufa; p2 = (ulv *) bufb; - printf("setting %3u", j); - consoleUpdate(NULL); + // printf("setting %3u", j); for (i = 0; i < count; i++) { *p1++ = *p2++ = (ul) UL_BYTE(j); } - printf("\b\b\b\b\b\b\b\b\b\b\b"); - printf("testing %3u", j); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("testing %3u", j); if (compare_regions(bufa, bufb, count)) { return -1; } } - printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); return 0; } @@ -311,13 +342,12 @@ int test_walkbits0_comparison(ulv *bufa, ulv *bufb, size_t count) { unsigned int j; size_t i; - printf(" "); + // printf(" "); for (j = 0; j < (UL_LEN * 2 / dividend / dividend); j++) { - printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); p1 = (ulv *) bufa; p2 = (ulv *) bufb; - printf("setting %3u", j); - consoleUpdate(NULL); + // printf("setting %3u", j); for (i = 0; i < count; i++) { if (j < UL_LEN) { /* Walk it up. */ *p1++ = *p2++ = ONE << j; @@ -325,14 +355,13 @@ int test_walkbits0_comparison(ulv *bufa, ulv *bufb, size_t count) { *p1++ = *p2++ = ONE << (UL_LEN * 2 - j - 1); } } - printf("\b\b\b\b\b\b\b\b\b\b\b"); - printf("testing %3u", j); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("testing %3u", j); if (compare_regions(bufa, bufb, count)) { return -1; } } - printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); return 0; } @@ -342,13 +371,12 @@ int test_walkbits1_comparison(ulv *bufa, ulv *bufb, size_t count) { unsigned int j; size_t i; - printf(" "); + // printf(" "); for (j = 0; j < (UL_LEN * 2 / dividend / dividend); j++) { - printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); p1 = (ulv *) bufa; p2 = (ulv *) bufb; - printf("setting %3u", j); - consoleUpdate(NULL); + // printf("setting %3u", j); for (i = 0; i < count; i++) { if (j < UL_LEN) { /* Walk it up. */ *p1++ = *p2++ = UL_ONEBITS ^ (ONE << j); @@ -356,14 +384,13 @@ int test_walkbits1_comparison(ulv *bufa, ulv *bufb, size_t count) { *p1++ = *p2++ = UL_ONEBITS ^ (ONE << (UL_LEN * 2 - j - 1)); } } - printf("\b\b\b\b\b\b\b\b\b\b\b"); - printf("testing %3u", j); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("testing %3u", j); if (compare_regions(bufa, bufb, count)) { return -1; } } - printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); return 0; } @@ -373,13 +400,12 @@ int test_bitspread_comparison(ulv *bufa, ulv *bufb, size_t count) { unsigned int j; size_t i; - printf(" "); + // printf(" "); for (j = 0; j < (UL_LEN * 2 / dividend / dividend); j++) { - printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); p1 = (ulv *) bufa; p2 = (ulv *) bufb; - printf("setting %3u", j); - consoleUpdate(NULL); + // printf("setting %3u", j); for (i = 0; i < count; i++) { if (j < UL_LEN) { /* Walk it up. */ *p1++ = *p2++ = (i % 2 == 0) @@ -393,14 +419,13 @@ int test_bitspread_comparison(ulv *bufa, ulv *bufb, size_t count) { | (ONE << (UL_LEN * 2 + 1 - j))); } } - printf("\b\b\b\b\b\b\b\b\b\b\b"); - printf("testing %3u", j); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("testing %3u", j); if (compare_regions(bufa, bufb, count)) { return -1; } } - printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); return 0; } @@ -411,28 +436,26 @@ int test_bitflip_comparison(ulv *bufa, ulv *bufb, size_t count) { ul q; size_t i; - printf(" "); + // printf(" "); for (k = 0; k < (UL_LEN / dividend / dividend); k++) { q = ONE << k; for (j = 0; j < 8; j++) { - printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); q = ~q; - printf("setting %3u", k * 8 + j); - consoleUpdate(NULL); + // printf("setting %3u", k * 8 + j); p1 = (ulv *) bufa; p2 = (ulv *) bufb; for (i = 0; i < count; i++) { *p1++ = *p2++ = (i % 2) == 0 ? q : ~q; } - printf("\b\b\b\b\b\b\b\b\b\b\b"); - printf("testing %3u", k * 8 + j); - consoleUpdate(NULL); + // printf("\b\b\b\b\b\b\b\b\b\b\b"); + // printf("testing %3u", k * 8 + j); if (compare_regions(bufa, bufb, count)) { return -1; } } } - printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); + // printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"); return 0; } @@ -444,7 +467,7 @@ int test_8bit_wide_random(ulv* bufa, ulv* bufb, size_t count) { unsigned int b, j = 0; size_t i; - putchar(' '); + // putchar(' '); for (attempt = 0; attempt < 2; attempt++) { if (attempt & 1) { p1 = (u8v *) bufa; @@ -460,17 +483,15 @@ int test_8bit_wide_random(ulv* bufa, ulv* bufb, size_t count) { *p1++ = *t++; } if (!(i % PROGRESSOFTEN)) { - putchar('\b'); - putchar(progress[++j % PROGRESSLEN]); - consoleUpdate(NULL); + // putchar('\b'); + // putchar(progress[++j % PROGRESSLEN]); } } if (compare_regions(bufa, bufb, count)) { return -1; } } - printf("\b \b"); - consoleUpdate(NULL); + // printf("\b \b"); return 0; } @@ -481,7 +502,7 @@ int test_16bit_wide_random(ulv* bufa, ulv* bufb, size_t count) { unsigned int b, j = 0; size_t i; - putchar( ' ' ); + // putchar( ' ' ); for (attempt = 0; attempt < 2; attempt++) { if (attempt & 1) { p1 = (u16v *) bufa; @@ -497,17 +518,15 @@ int test_16bit_wide_random(ulv* bufa, ulv* bufb, size_t count) { *p1++ = *t++; } if (!(i % PROGRESSOFTEN)) { - putchar('\b'); - putchar(progress[++j % PROGRESSLEN]); - consoleUpdate(NULL); + // putchar('\b'); + // putchar(progress[++j % PROGRESSLEN]); } } if (compare_regions(bufa, bufb, count)) { return -1; } } - printf("\b \b"); - consoleUpdate(NULL); + // printf("\b \b"); return 0; } #endif diff --git a/Source/MemTesterNX/source/tests.h b/Source/MemTesterNX/source/tests.h index e8b2f5ff..e4990abb 100755 --- a/Source/MemTesterNX/source/tests.h +++ b/Source/MemTesterNX/source/tests.h @@ -37,3 +37,6 @@ int test_8bit_wide_random(unsigned long volatile *bufa, unsigned long volatile * int test_16bit_wide_random(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); #endif +int test_stress_memcpy(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int test_stress_memset(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int test_stress_memcmp(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); \ No newline at end of file