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 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
|
/*
* mem.c: memory allocation wrappers with a few simple checks
*
* Copyright (c) 2002-2009 Dennis Stosberg <dennis@stosberg.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License,
* version 2 as published by the Free Software Foundation
*/
#include "mem.h"
#ifdef MEMDEBUG
static void meminfo_add(void *p, size_t size, const char *file, int line);
static void meminfo_rm(void *p, const char *file, int line);
static MEMINFO *meminfo_getinfo(void *p);
static void print_memory_stats(void);
static void die(const char *format, ...);
static void warn(const char *format, ...);
static size_t mem_num_malloced = 0;
static size_t mem_bytes_malloced = 0;
static size_t mem_num_freed = 0;
static size_t mem_bytes_freed = 0;
static MEMINFO **meminfo = NULL;
static size_t meminfo_size = 0;
static size_t meminfo_count = 0;
static unsigned char magic[] = { 0xFE, 0xDC, 0xBA, 0x98,
0x76, 0x54, 0x32, 0x10 };
/**
* Adds information about an newly allocated memory region to the
* meminfo structure.
*/
static void meminfo_add(void *p, size_t size, const char *file, int line) {
if(!meminfo)
if(atexit(print_memory_stats))
warn("Memory statistics will not be shown.");
/* enlarge MEMINFO structure if necessary */
if(meminfo_count == meminfo_size) {
meminfo_size = meminfo_size ? meminfo_size << 1 : 1;
meminfo = realloc(meminfo, meminfo_size * sizeof(MEMINFO*));
}
/* add information to structure */
meminfo[meminfo_count] = malloc(sizeof(MEMINFO));
meminfo[meminfo_count]->addr = p;
meminfo[meminfo_count]->size = size;
meminfo[meminfo_count]->file = file;
meminfo[meminfo_count]->line = line;
meminfo_count++;
#ifdef MEMINFO_VERBOSE
printf("allocated 0x%x at %s:%d\n", p, file, line);
#endif
mem_bytes_malloced += size;
mem_num_malloced++;
}
/**
* Removes information about a just freed memory region to the
* meminfo structure. Halts the program if the region to be freed
* has never been allocated at all.
*/
static void meminfo_rm(void *p, const char *file, int line) {
size_t i;
for(i = 0; i < meminfo_count; ++i) {
if(p == meminfo[i]->addr) {
#ifdef MEMINFO_VERBOSE
fprintf(stderr, "freed 0x%x at %s:%d\n",
p, file, line);
fprintf(stderr, " was allocated at %s:%d\n",
meminfo[i]->file, meminfo[i]->line);
#endif
mem_bytes_freed += meminfo[i]->size;
free(meminfo[i]);
meminfo[i] = meminfo[meminfo_count - 1];
meminfo_count--;
mem_num_freed++;
return;
}
}
die("Tried to free a piece of memory at 0x%p in %s:%d"
" which is not allocated", (void*)p, file, line);
}
/**
* Returns the size of a malloc'ed region.
*/
static MEMINFO *meminfo_getinfo(void *p) {
size_t i;
for(i = 0; i < meminfo_count; ++i)
if(p == meminfo[i]->addr)
return(meminfo[i]);
return(0);
}
/**
* Prints statistics about allocated and deallocated memory to
* stderr just before the program exits. If there are memory
* regions left that were allocated but never freed, it will print
* information on those regions as well.
*/
static void print_memory_stats(void) {
size_t i;
if(mem_num_malloced == mem_num_malloced
&& mem_bytes_malloced == mem_bytes_freed)
return;
fprintf(stderr, "Memory statistics: \n"
" %lu allocations (%lu bytes)\n"
" %lu deallocations (%lu bytes)\n"
" %ld difference (%ld bytes)\n",
(unsigned long)mem_num_malloced,
(unsigned long)mem_bytes_malloced,
(unsigned long)mem_num_freed,
(unsigned long)mem_bytes_freed,
(long)(mem_num_malloced - mem_num_freed),
(long)(mem_bytes_malloced - mem_bytes_freed)
);
if(meminfo_count) {
fprintf(stderr, "%lu malloc'ed regions were never "
"free'd:\n", (unsigned long)meminfo_count);
for(i = 0; i < meminfo_count; ++i) {
fprintf(stderr, " %p (%lu bytes) allocated at %s:%d\n",
(void *)meminfo[i]->addr,
(unsigned long)meminfo[i]->size,
meminfo[i]->file, meminfo[i]->line);
free(meminfo[i]);
}
free(meminfo);
}
}
/**
* Allocates memory and records information on the allocated
* memory. It will halt the program if the memory region to be
* allocated has a size of zero.
*/
void *ymalloc_dbg(size_t size, const char *file, int line) {
void *p;
if(!size)
die("Trying to allocate 0 bytes at %s:%d", file, line);
p = malloc(size + 2*sizeof(magic));
if(!p)
die("Out of memory at %s:%d while trying to allocate %lu bytes",
file, line, (unsigned long)size);
meminfo_add(p, size, file, line);
memcpy(p, &magic, sizeof(magic));
memcpy((char*)p + size + sizeof(magic), &magic, sizeof(magic));
return((char*)p + sizeof(magic));
}
/**
* Frees memory and removes information on the memory region. Halts
* the program, if the region to be freed has never been allocated
* at all.
*/
void yfree_dbg(void *p, const char *file, int line) {
MEMINFO *info;
if(!p)
die("Trying to free null pointer at %s:%d", file, line);
info = meminfo_getinfo((char*)p - sizeof(magic));
if(info == NULL) {
die("Looks like we were asked to free a piece of memory"
" that we never allocated: %s:%d", file, line);
}
/* Check, if region boundaries are intact. */
if(memcmp((char*)p - sizeof(magic), &magic, sizeof(magic))
|| memcmp((char*)p + info->size, &magic, sizeof(magic))) {
die("The boundaries of a region allocated at %s:%d were "
"overwritten.", info->file, info->line);
}
free((char*)p - sizeof(magic));
meminfo_rm((char*)p - sizeof(magic), file, line);
p = NULL;
}
/**
* Allocates memory and records information on the allocated
* memory. This function will halt the program if the memory
* region to be allocated has a size of zero.
*/
void *ycalloc_dbg(size_t number, size_t size, const char *file, int line) {
void *p;
p = ymalloc_dbg(number * size, file, line);
memset(p, 0, number * size);
return(p);
}
/**
* Reallocates memory and updates the information on the memory
* region. This function will halt the program if new size of the
* memory region is zero ot the region has never been allocated
* with ymalloc or ycalloc.
*/
void *yrealloc_dbg(void *p, size_t size, const char *file, int line) {
MEMINFO *area_info;
if(!size) {
die("Trying to reallocate 0 bytes at %s:%d", file, line);
}
if(!p) {
p = ymalloc_dbg(size, file, line);
return(p);
}
/* Check, if region boundaries are intact. */
area_info = meminfo_getinfo((char*)p - sizeof(magic));
if(memcmp((char*)p - sizeof(magic), &magic, sizeof(magic)) ||
memcmp((char*)p + area_info->size, &magic, sizeof(magic))) {
die("The boundaries of a region allocated at %s:%d were "
"overwritten.", area_info->file, area_info->line);
}
meminfo_rm((char*)p - sizeof(magic), file, line);
p = realloc((char*)p - sizeof(magic), size + 2*sizeof(magic));
if(!p)
die("Out of memory at %s:%d while trying to allocate %lu bytes",
file, line, (unsigned long)size);
/* realloc has copied the magic at the beginning of the
memory area. We need to set the final magic only */
memcpy((char*)p + size + sizeof(magic), &magic, sizeof(magic));
meminfo_add(p, size, file, line);
return((char*)p + sizeof(magic));
}
/**
* Prints an error message to stdout and halts the program
*/
static void warn(const char *format, ...) {
va_list argp;
va_start(argp, format);
vfprintf(stderr, format, argp);
va_end(argp);
fprintf(stderr, "\n");
}
/**
* Prints an error message to stdout and halts the program
*/
static void die(const char *format, ...) {
va_list argp;
va_start(argp, format);
vfprintf(stderr, format, argp);
va_end(argp);
fprintf(stderr, "\n");
exit(EXIT_FAILURE);
}
#endif
|