1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
/*
* my_malloc.c
*
* This file provides an interface to malloc() and free() which
*
* (1) Keeps track of the number of calls to malloc() minus the number
* of calls to free(). This allows easy detection of programming
* errors which allocate more memory than they free (or vice versa).
*
* (2) Aborts the program if malloc() returns NULL. This saves having
* to check whether malloc() == NULL at each point where malloc()
* is called.
*
* All kernel routines use my_malloc() and my_free();
* no UI routines do so.
*
* The UI should call verify_my_malloc_usage() upon exit, to verify
* that the number of calls to my_malloc() was exactly balanced by
* the number of calls to my_free().
*
* The remainder of this comment deals with a debugging feature, and
* may be safely ignored until such time as you start freeing memory
* you haven't allocated, or writing off the ends of arrays.
*
* If the constant DEBUG_MALLOC is #defined to be 1 (see below) then
* my_malloc() and my_free() maintain a linked list
* of the addresses of the blocks of memory which have been allocated.
* If some other part of the kernel attempts to free memory which has
* not been allocated (or free the same memory twice), free() generates
* and error message and exits. Because this feature is for debugging
* purposes only, no attempt is made to be efficient. (Obviously a
* binary tree would be more efficient than a linked list, but you'd have
* to account for the fact that the memory is most likely allocated
* in linear order. Reversing the order of the bytes would be simpler
* than implementing a balanced tree.)
*
* Note that the DEBUG_MALLOC feature itself uses memory, but does not
* record its own usage in the linked list. This is OK. Its purpose
* is to help debug SnapPea, not malloc().
*
* If DEBUG_MALLOC is turned on, my_malloc() tacks four extra bytes on
* the end of every requested block of memory, and writes in an
* arbitrary (but well defined) sequence of four characters. When the
* memory is freed, my_free() checks those four characters to see whether
* they've been overwritten. This is not a perfect guarantee against
* writing past the ends of array, but it should detect at least some
* errors.
*/
#include "kernel.h"
#include <stdlib.h> /* needed for malloc() */
#include <stdio.h> /* needed for sprintf() */
static int net_malloc_calls = 0;
/*
* The debugging feature is normally off.
*/
#define DEBUG_MALLOC 1
#if DEBUG_MALLOC
#define MAX_BYTES 50000
typedef struct memnode
{
void *address;
size_t bytes;
struct memnode *next;
} MemNode;
static MemNode mem_list = {NULL, 0, NULL};
static const char secret_code[5] = "Adam";
static Boolean message_given = FALSE;
#endif
void *my_malloc(
size_t bytes)
{
void *ptr;
#if DEBUG_MALLOC
MemNode *new_mem_node;
char *error_bytes;
int i;
#endif
#if DEBUG_MALLOC
if (message_given == FALSE)
{
uAcknowledge("The my_malloc() memory allocator is in debugging mode.");
message_given = TRUE;
}
#endif
#if DEBUG_MALLOC
if (bytes < 0)
{
uAcknowledge("A negative number of bytes were requested in my_malloc().");
exit(3);
}
if (bytes > MAX_BYTES)
uAcknowledge("Too many bytes were requested in my_malloc().");
#endif
/*
* Most likely malloc() and free() would correctly handle
* a request for zero bytes, but why take chances?
*/
if (bytes == 0)
bytes = 1;
#if DEBUG_MALLOC
ptr = malloc(bytes + 4);
#else
ptr = malloc(bytes);
#endif
if (ptr == NULL)
uAbortMemoryFull();
net_malloc_calls++;
#if DEBUG_MALLOC
error_bytes = (char *) ptr + bytes;
for (i = 0; i < 4; i++)
error_bytes[i] = secret_code[i];
new_mem_node = (MemNode *) malloc((size_t) sizeof(MemNode));
if (new_mem_node == NULL)
{
uAcknowledge("out of memory");
exit(4);
}
new_mem_node->address = ptr;
new_mem_node->bytes = bytes;
new_mem_node->next = mem_list.next;
mem_list.next = new_mem_node;
#endif
return ptr;
}
void my_free(
void *ptr)
{
#if DEBUG_MALLOC
Boolean old_node_found;
MemNode *old_mem_node,
*prev_mem_node;
size_t bytes;
char *error_bytes;
int i;
#endif
#if DEBUG_MALLOC
old_node_found = FALSE;
for ( prev_mem_node = &mem_list, old_mem_node = mem_list.next;
old_mem_node != NULL;
old_mem_node = old_mem_node->next, prev_mem_node = prev_mem_node->next)
if (old_mem_node->address == ptr)
{
old_node_found = TRUE;
bytes = old_mem_node->bytes;
prev_mem_node->next = old_mem_node->next;
free(old_mem_node);
break;
}
if (old_node_found == FALSE)
{
uAcknowledge("A bad address was passed to my_free().");
exit(5);
}
error_bytes = (char *) ptr + bytes;
for (i = 0; i < 4; i++)
if (error_bytes[i] != secret_code[i])
{
uAcknowledge("my_free() received a corrupted array.");
exit(6);
}
#endif
free(ptr);
net_malloc_calls--;
}
int malloc_calls()
{
return net_malloc_calls;
}
void verify_my_malloc_usage()
{
char the_message[256];
if (net_malloc_calls != 0)
{
sprintf(the_message, "Memory allocation error:\rThere were %d %s calls to my_malloc() than to my_free().",
net_malloc_calls > 0 ? net_malloc_calls : - net_malloc_calls,
net_malloc_calls > 0 ? "more" : "fewer");
uAcknowledge(the_message);
}
}
|