malloc/free test program
nw at vaxine.UUCP
nw at vaxine.UUCP
Tue Feb 17 06:49:49 AEST 1987
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
More information about the Comp.sources.unix
mailing list