/* 
 * testmem
 * 
 * (c) Marcus Engene 2006 (myfirstname@mylastname.se)
 *     BSD license.
 * 
 * If you have a server that is colocated that you cannot run memtest86 
 * on (memtest86.com) you could try this program.
 * 
 * Imagine you have 1GB memory and 1GB swap and most of your processes
 * are idling. If you allocate say a 800MB block and read and write this
 * block some times, likely this block will be in memory and the other
 * stuff will be paged out. The memory address that might/will fail is 
 * irrelevant as MMU likely is present.
 * 
 * If you allocate a block too large to fit into memory (eg larger than
 * RAM size) the block will be paged to disk. This might we something you
 * want as the swap is tested then.
 *
 * Please mail me if you think there are test cases missing.
 * Main home of this file will be http://engene.se/testmem
 * 
 * Thanks to Johan Linden for test4() (address lines broken test)
 */
#include <stdio.h>
#include <stdlib.h>

/**
 * Fills memory with 0 & check if memory is 0.
 * Fill first, check after. (swappable)
 * 
 * @return 0 ok, -1 F
 */
int test1 (size_t memsize, unsigned char * ptr)
{
    int retval = 0;
    int nbr_errors = 0;
    size_t i;
    
    // Set mem to 0
    for (i=0; i<memsize; i++) {
        ptr[i] = 0;
    }
    
    // is memory 0?
    for (i=0; i<memsize; i++) {
        if (0 != ptr[i]) 
        {
            if (10 < nbr_errors) {
                fprintf(stderr,"test1() Too many errors\n");
                return -1;
            }
            fprintf(stderr,"test1() (write 0) Error at mempos %p\n", &(ptr[i]));
            retval = -1;
            nbr_errors++;
        }
    }
    
    return retval;
}


/**
 * Fills memory with 1:s.
 * Fill first then check. (swappable)
 * 
 * @return 0 ok, -1 F
 */
int test2 (size_t memsize, unsigned char * ptr)
{
    int retval = 0;
    int nbr_errors = 0;
    size_t i;

    // Set mem to 1
    for (i=0; i<memsize; i++) {
        ptr[i] = 0xff;
    }
    
    // is memory 1?
    for (i=0; i<memsize; i++) {
        if (0xff != ptr[i]) 
        {
            if (10 < nbr_errors) {
                fprintf(stderr,"test2() Too many errors\n");
                return -1;
            }
            fprintf(stderr,"test2() (write 1) Error at mempos %p\n", &(ptr[i]));
            retval = -1;
            nbr_errors++;
        }
    }
    
    return retval;
}


/**
 * Fills memory with 0x55 & check if memory is 0.
 * Fill first, check after. (swappable)
 * 
 * @return 0 ok, -1 F
 */
int test3 (size_t memsize, unsigned char * ptr)
{
    int retval = 0;
    int nbr_errors = 0;
    size_t i;

    // Set mem to 0x55
    for (i=0; i<memsize; i++) {
        ptr[i] = 0x55;
    }
    
    // is memory 0?
    for (i=0; i<memsize; i++) {
        if (0x55 != ptr[i]) 
        {
            if (10 < nbr_errors) {
                fprintf(stderr,"test3() Too many errors\n");
                return -1;
            }
            fprintf(stderr,"test3() (write 0x55) Error at mempos %p\n", &(ptr[i]));
            retval = -1;
            nbr_errors++;
        }
    }
    
    return retval;
}

/**
 * Fills memory with index & 0xff.
 * Fill first, check after. (swappable)
 * 
 * @return 0 ok, -1 F
 */
int test4 (size_t memsize, unsigned char * ptr)
{
    int retval = 0;
    int nbr_errors = 0;
    size_t i;

    // Set mem to 0x55
    for (i=0; i<memsize; i++) {
        ptr[i] = i & 0xff;
    }
    
    // is memory 0?
    for (i=0; i<memsize; i++) {
        if ((i & 0xff) != ptr[i]) 
        {
            if (10 < nbr_errors) {
                fprintf(stderr,"test4() Too many errors\n");
                return -1;
            }
            fprintf(stderr,"test4() (write idx & 0xff) Error at mempos %p\n", &(ptr[i]));
            retval = -1;
            nbr_errors++;
        }
    }
    
    return retval;
}

/**
 * Fills memory with index.
 * Fill first, check after. (swappable)
 * 
 * @return 0 ok, -1 F
 */
int test5 (size_t memsize, unsigned char * ptr)
{
    int retval = 0;
    int nbr_errors = 0;
    size_t i;
    size_t *sptr = (size_t *)ptr;
    
    memsize = memsize / sizeof(size_t);

    // Set mem to 0x55
    for (i=0; i<memsize; i++) {
        sptr[i] = i;
    }
    
    // is memory 0?
    for (i=0; i<memsize; i++) {
        if (i != sptr[i]) 
        {
            if (10 < nbr_errors) {
                fprintf(stderr,"test5() Too many errors\n");
                return -1;
            }
            fprintf(stderr,"test5() (write idx) Error at mempos %p\n", &(ptr[i]));
            retval = -1;
            nbr_errors++;
        }
    }
    
    return retval;
}

int main(int argc, char **argv)
{
    size_t memsize = 8;
    unsigned char *ptr = 0;
    int loop = 1;
    int i;
    size_t lenstr = 0;
    
    if (2 > argc) {
        fprintf (stderr,"Usage: %s chunksize [loops]\n\n  chunksize size in bytes of chunk to test\n  loops nbr of times to do tests\n\n", argv[0]);
        exit(1);
    }
    
    memsize = (size_t) atoll (argv[1]);
    lenstr = strlen (argv[1]);
    lenstr--;
    if ('b' == argv[1][lenstr] || 'B' == argv[1][lenstr]) {
        if (1 < lenstr) {
            lenstr--;
        }
    }
    if ('k' == argv[1][lenstr] || 'K' == argv[1][lenstr]) {
        memsize *= (1024);
    }
    if ('m' == argv[1][lenstr] || 'M' == argv[1][lenstr]) {
        memsize *= (1024*1024);
    }
    if ('g' == argv[1][lenstr] || 'G' == argv[1][lenstr]) {
        memsize *= (1024*1024*1024);
    }
    
    if (2 < argc) {
        loop = atoi (argv[2]);
    }
    
    if (8 > memsize) {
        memsize = 8;
    }

    if (0 == (ptr = malloc (memsize))) {
        fprintf(stderr,"Failed to alloc %ld bytes\n", memsize);
        exit (1);
    }

    fprintf(stderr,"Testing %ld bytes\n", memsize);
    for (i=0; i<loop; i++) {
        fprintf(stderr,"Tests iteration %d\n", i);
        test1(memsize, ptr);
        test2(memsize, ptr);
        test3(memsize, ptr);
        test4(memsize, ptr);
        test5(memsize, ptr);
    }

    /* Should I free first? According to discussion on OpenBSD site
     * free() means mem is paged in again, just die and it's not
     */
    /* free (ptr); */
    
    return 0;
}


