[net.sources] malloc/free test program

nw@vaxine.UUCP (02/16/87)

A while back I asked the net if anyone had a malloc/free test program that
would allocate and free randomly sized pieces of memory with random lifetimes
(and fill them with patterns that would be checked for corruption).  I got
a number of useful suggestions (best one: use the "diff" program as a
malloc test ... it's brutal).  Eventually, I wound up writing my own
test program, which actually helped me find a bug.

On the assumption that it might be useful to someone else, here it is.
Please note:

        - No documentation (read the code to find out what it does).
        - Could be extended in zillions of ways to make it more useful.
        - No makefile, just compile "cc mtest.c"

A "shar" archive follows.

Neil Webber     Automatix Inc. (or current resident)    Billerica MA
        {decvax,ihnp4,allegra}!encore!vaxine!nw

---------------------- cut here -------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the result in a file
# 3. Execute the file with /bin/sh (not csh)
echo extracting - mtest.c
sed 's/^X//' > mtest.c << 'FRIDAY_NIGHT'
X#include <stdio.h>
X
Xextern int      atoi ();
Xextern long     random ();
Xextern char    *sbrk ();
X
Xextern char    *malloc ();
Xextern char    *realloc ();
Xextern int      free ();
X
Xstruct memevent {
X        int                     m_time;         /* time to go */
X        char                   *m_memory;       /* malloc'ed mem */
X        unsigned                m_size;         /* size of mem */
X        int                     m_id;           /* id, for trace/debug */
X        int                     m_realloc;      /* counter, for debugging */
X        char                    m_pattern;      /* pattern in memory */
X        struct memevent        *m_next;         /* linked list pointer */
X};
X
X#ifndef MAX_EVENTS
X#define MAX_EVENTS      10000
X#endif
X
Xstruct memevent eventpool[ MAX_EVENTS ];
X
Xstruct memevent *events;
Xstruct memevent *free_events;
X
Xchar stdout_buf[ BUFSIZ ];
Xchar stderr_buf[ BUFSIZ ];
X
Xint time_to_go;
Xint new_probability;
Xint realloc_probability = 25;           /* XXX: should set from argv */
Xint stat_frequency;
X
Xmain (argc, argv)
Xint argc;
Xchar *argv[];
X{
X        init (argc, argv);
X        run_test ();
X}
X
X/*
X * run_test ()
X *
X * Run the actual memory test.
X */
X
Xrun_test ()
X{
X        while (time_to_go > 0) {
X                arrival ();
X                service ();
X                -- time_to_go;
X                if ((time_to_go % stat_frequency) == 0)
X                        do_stats ();
X        }
X}
X
X/*
X * arrival ()
X *
X * With probability new_probability/100, allocate a new piece
X * of memory with some randomly determined size and lifetime,
X * and add it to the memory event list.
X */
X
Xarrival ()
X{
X        if (free_events && odds (new_probability, 100)) {
X                register struct memevent *m;
X                register char *p;
X
X                m = free_events;
X                free_events = m->m_next;
X                m->m_next = NULL;
X
X                                        /* XXX: let these be set from argv */
X                m->m_size = (unsigned) random_range (1, 100);
X                if (time_to_go < 100)
X                        m->m_time = random_range (1, time_to_go);
X                else
X                        m->m_time = random_range (1, 100);
X
X                m->m_pattern = (char) random_range (0, 127);
X                m->m_realloc = 0;
X                m->m_memory = malloc (m->m_size);
X                if (! m->m_memory)
X                        out_of_memory ();
X
X
X                for (p = m->m_memory; p < & m->m_memory[ m->m_size ]; p++)
X                        *p = m->m_pattern;
X
X                add_to_events (m);
X        }
X} /* arrival */
X
X/*
X * do_stats ()
X */
X
Xdo_stats ()
X{
X        register struct memevent *m;
X        int i;
X        long total;
X
X        printf ("---------------------\nTIME Remaining: %d\n", time_to_go);
X
X        /* print other interesting but implementation-dependent stuff here
X           (like count of blocks in heap, size of heap, etc) */
X
X        total = 0;
X        for (i = 1, m = events; m != NULL; m = m->m_next, i++) {
X                printf ("EVENT %5d (id %5d): ", i, m->m_id);
X                printf ("SIZE %4d, ", m->m_size);
X                printf ("PATTERN 0x%02x, ", m->m_pattern & 0xFF);
X                printf ("TIME %4d ", m->m_time);
X                if (m->m_realloc > 0)
X                        printf ("REALLOC %d", m->m_realloc);
X                printf ("\n");
X                total += m->m_size;
X        }
X        printf ("TOTAL events %d, allocated memory %d\n", i-1, total);
X        (void) fflush (stdout);
X} /* do_stats */
X
X/*
X * service ()
X *
X * Decrement the time remaining on the head event.  If
X * it's time is up (zero), service it.
X *
X * Servicing an event generally means free'ing it (after checking
X * for corruption).  It is also possible (realloc_probability) to
X * realloc the event instead.
X */
X
Xservice ()
X{
X        register struct memevent *m;
X
X        if ((m = events) != NULL)
X                -- m->m_time;
X
X        while (m != NULL && m->m_time == 0) {
X                register char *p;
X
X                for (p = m->m_memory; p < & m->m_memory[ m->m_size ]; p++) {
X                        if (*p != m->m_pattern)
X                                corrupted ();
X                }
X
X                events = m->m_next;     /* delete this event */
X
X                if (time_to_go > 1 && odds (realloc_probability, 100))
X                        realloc_event (m);
X                else
X                        free_event (m);
X
X                m = events;
X
X        }
X} /* service */
X
X/*
X * free_event (m)
X *
X * Called to free up the given event, including its memory.
X */
X
Xfree_event (m)
Xregister struct memevent *m;
X{
X        free (m->m_memory);
X        m->m_next = free_events;
X        free_events = m;
X}
X
X/*
X * realloc_event (m)
X *
X * Called from service(), to reallocate an event's memory,
X * rather than freeing it.
X */
X
Xrealloc_event (m)
Xregister struct memevent *m;
X{
X        register char *p;
X        unsigned new_size;
X        unsigned min_size;
X
X                                        /* XXX: let these be set from argv */
X        new_size = (unsigned) random_range (1, 100);
X
X        ++ m->m_realloc;                /* for stats */
X        m->m_memory = realloc (m->m_memory, new_size);
X        if (! m->m_memory)
X                out_of_memory ();
X
X        m->m_next = NULL;
X
X        if (time_to_go < 100)
X                m->m_time = random_range (1, time_to_go - 1);
X        else
X                m->m_time = random_range (1, 100);   /* XXX: should set from argv */
X
X        min_size = new_size > m->m_size ? m->m_size : new_size;
X
X        for (p = m->m_memory; p < & m->m_memory[ min_size ]; p++) {
X                if (*p != m->m_pattern)
X                        corrupted ();
X        }
X
X        m->m_size = new_size;
X        for (p = m->m_memory; p < & m->m_memory[ m->m_size ]; p++)
X                *p = m->m_pattern;
X
X
X        add_to_events (m);
X} /* realloc_event */
X
X/*
X * add_to_events (m)
X *
X * Add the given event structure onto the time-ordered event list.
X */
X
Xadd_to_events (m)
Xregister struct memevent *m;
X{
X        register struct memevent *l;
X        register struct memevent *ol;
X
X        for (ol = NULL, l = events; l != NULL; ol = l, l = l->m_next) {
X                if (l->m_time > m->m_time) {
X                        if (ol == NULL) {
X                                m->m_next = events;
X                                events = m;
X                        }
X                        else {
X                                m->m_next = l;
X                                ol->m_next = m;
X                        }
X
X                        l->m_time -= m->m_time;
X                        return;
X                }
X
X                m->m_time -= l->m_time;
X        }
X
X        if (events == NULL)
X                events = m;
X        else
X                ol->m_next = m;
X} /* add_to_events */
X
X/*
X * init_events ()
X *
X * Set up the memevent pools.
X */
X
Xinit_events ()
X{
X        register struct memevent *m;
X        int i;
X
X        for (i = 0, m = eventpool; m < & eventpool[ MAX_EVENTS ]; m++, i++) {
X                m->m_id = i;
X                m->m_next = m + 1;
X        }
X
X        eventpool[ MAX_EVENTS-1 ].m_next = NULL;
X
X        free_events = eventpool;
X}
X
X/*
X * init (argc, argv)
X *
X * Initialize the memory tests.
X */
X
Xinit (argc, argv)
Xint argc;
Xchar *argv[];
X{
X	if (argc != 4) {
X		fprintf (stderr, "usage: %s new_prob time_to_go stat_freq\n", argv[ 0 ]);
X		exit (1);
X	}
X        new_probability = atoi (argv[ 1 ]);
X        time_to_go = atoi (argv[ 2 ]);
X        stat_frequency = atoi (argv[ 3 ]);
X
X        srandom (1);
X
X        init_events ();
X
X        /*
X         * Use statically allocated buffers, otherwise
X         * stdio() will call malloc to allocate buffers, and
X         * this gets confusing when debugging stuff.
X         */
X
X        setbuf (stdout, stdout_buf);
X        setbuf (stderr, stderr_buf);
X}
X
X/*
X * XXX: Should really send SIGQUIT ...
X */
X
Xcause_core_dump ()
X{
X        * (long *) 1 = 5;
X}
X
Xcorrupted ()
X{
X        printf ("Corrupted\n");
X        cause_core_dump ();
X}
X
Xout_of_memory ()
X{
X        printf ("Out of memory!\n");
X        cause_core_dump ();
X}
X
X/*
X * odds (m, n)
X *
X * Return TRUE (non-zero) with probability m out of n.
X */
X
Xodds (m, n)
Xint m;
Xint n;
X{
X        return ((random () % n) < m);
X}
X
X/*
X * random_range (lo, hi)
X *
X * Pick a random integer from lo to hi (inclusive).
X */
X
Xrandom_range (lo, hi)
Xint lo;
Xint hi;
X{
X        return ((random () % (hi - lo + 1)) + lo);
X}
X
X#if DBG
X/*
X * de_cmpf (m1,m2)
X *
X * compare function for qsort() in dump_events.
X * Sort by memory address of the memory allocated to
X * the event.
X */
X
Xint
Xde_cmpf (m1, m2)
Xstruct memevent **m1;
Xstruct memevent **m2;
X{
X        unsigned long maddr1 = (unsigned long) (*m1)->m_memory;
X        unsigned long maddr2 = (unsigned long) (*m2)->m_memory;
X
X                                        /* sloppy */
X        return (maddr1 - maddr2);
X}
X#endif DBG
X
X/*
X * dump_events ()
X *
X * Useful for debugging.
X */
X
X#if DBG
Xdump_events ()
X{
X        static struct memevent *sorted[ MAX_EVENTS ];
X        register struct memevent *m;
X        register int i;
X
X        fprintf (stderr, "DUMP EVENTS (time remaining = %d)\n", time_to_go);
X
X        for (m = events, i = 0; m != NULL; m = m->m_next, i++)
X                sorted[ i ] = m;
X
X        if (i == 0) {
X                fprintf (stderr, "No events.\n");
X                return;
X        }
X
X        qsort ((char *) sorted, i, sizeof (struct memevent *), de_cmpf);
X
X        sorted[ i ] = 0;
X        for (i = 0, m = sorted[ 0 ]; m != NULL; m = sorted[ ++i ]) {
X                fprintf (stderr, "E# %3d: ", m->m_id);
X                fprintf (stderr, "SIZ%4d, ", m->m_size);
X                fprintf (stderr, "RANGE: 0x%08x -- 0x%08x ",
X                                m->m_memory, m->m_memory + m->m_size - 1);
X                (void) fflush (stderr);
X
X                                        /* Peek at the surrounding longs,
X                                           for debugging a particular malloc
X                                           implementation.  Your choices may
X                                           vary. */
X
X                fprintf (stderr, "BOUNDARY TAGS: %4d ", * (long *) (m->m_memory - 4));
X                (void) fflush (stderr);
X                fprintf (stderr, "%4d\n", * (long *) ((m->m_memory - 8) - (* (long *) (m->m_memory - 4))));
X                (void) fflush (stderr);
X        }
X        fprintf (stderr, "END DUMP_EVENTS\n");
X        (void) fflush (stderr);
X} /* dump_events */
X#endif DBG
FRIDAY_NIGHT