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