comparison urogue/memory.c @ 256:c495a4f288c6

Import UltraRogue from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Tue, 31 Jan 2017 19:56:04 -0500
parents
children c4b12d2d1dcd
comparison
equal deleted inserted replaced
253:d9badb9c0179 256:c495a4f288c6
1 /*
2 memory.c
3
4 UltraRogue: The Ultimate Adventure in the Dungeons of Doom
5 Copyright (C) 1995 Herb Chong
6 All rights reserved.
7
8 See the file LICENSE.TXT for full copyright and licensing information.
9 */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13
14 #include "dict.h"
15 #include "memory.h"
16 #include "rogue.h"
17
18 static char sccsid[] = "%W%\t%G%";
19
20 /* Debugging memory allocation code that tries to trap common memory problems
21 like overwriting storage and stepping on memory pointer chains. If code
22 doesn't use malloc, free, and realloc a lot, these routines can be left in
23 as added protection against undetected storage bugs.
24 */
25
26 /* FENCE_SIZE should be a multiple of sizeof(size_t) to prevent alignment problems.
27 The code assumes that malloc and realloc return pointers aligned at least on size_t
28 sized boundaries and that a pointer needs alignment no more strict than that of an
29 object needed to hold a size_t.
30 */
31
32 #define FENCE_SIZE (sizeof(size_t) * 1024)
33
34 static int memdebug_level = 0;
35 static DICTIONARY *allocations = NULL;
36 static FILE *trace_file = NULL;
37
38 /* set the debug level */
39 void mem_debug(const int level)
40 {
41 memdebug_level = level;
42
43 if (trace_file == NULL)
44 trace_file = fopen("trace", "w");
45
46 /* all except 0, 1, and unknown fall through */
47 switch(memdebug_level) {
48 case 2:
49 fprintf(trace_file, "+++ Memory tracking possible, ");
50 case 1:
51 fprintf(trace_file, "+++ Memory debugging enabled, ");
52 break;
53 case 0:
54 fprintf(trace_file, "+++ Memory debugging disabled, ");
55 break;
56 default:
57 fprintf(trace_file, "!!! Unknown memory debug level set, enabling level 1, ");
58 memdebug_level = 1;
59 break;
60 }
61 fprintf(trace_file, "fence size = %d\n", FENCE_SIZE);
62 }
63
64 /* set memory tracking on or off */
65 /* turning it off deletes all tracking data */
66 void mem_tracking(int flag)
67 {
68 /* do nothing if debuglevel is too low */
69 if (memdebug_level < 2)
70 return;
71
72 /* turn on tracking */
73 if (flag > 0) {
74 if (allocations != NULL) {
75 dict_destroy(allocations);
76 allocations = NULL;
77 }
78 allocations = dict_create(8, 100, 4, 20);
79 if (allocations == NULL) {
80 fprintf(trace_file, "!!! Unable to allocate tracking table!\n");
81 abort();
82 }
83 }
84 /* turn off tracking */
85 else if (allocations != NULL) {
86 dict_destroy(allocations);
87 allocations = NULL;
88 }
89 }
90
91 /* go through all pointers and see if they are OK, aborting if not */
92 /* always returns 1 if not aborting so that it can be included in */
93 /* if statement boolean expressions */
94 int mem_check(char *fname, int linenum)
95 {
96 STRING_ENTRY *se;
97
98 /* scan of a NULL dictionary always succeeds */
99 if (allocations == NULL)
100 return TRUE;
101
102 if (!dict_scan_begin(allocations)) {
103 fprintf(trace_file, "!!! Dictionary scan initialization failed!\n");
104 abort();
105 }
106
107 fprintf(trace_file, "\n+++ --- Starting pointer scan\n");
108 fprintf(trace_file, "+++ --- At %s, %d\n", fname, linenum);
109
110 /* mem_validate aborts if there is a problem */
111 while((se = dict_scan_next(allocations)) != NULL)
112 mem_validate(se->any_ptr);
113
114 fprintf(trace_file, "+++ --- Done pointer scan\n\n");
115
116 /* always return a good value if execution arrives here */
117 return 1;
118 }
119
120 /* allocate some memory and initialize header and trailer */
121 void *mem_malloc(const size_t bytes)
122 {
123 char *mem_temp;
124 size_t real_size = bytes + (FENCE_SIZE << 1);
125
126 /* allocate including guard bytes to detect some ways of overwriting of memory areas */
127 mem_temp = (void *)malloc(real_size);
128 if (memdebug_level > 0) {
129 fprintf(trace_file, "+++ Requested size of %ld bytes\n", bytes);
130 fprintf(trace_file, "+++ Actual malloc of %ld bytes located at %p\n", real_size, mem_temp);
131 }
132
133 /* if allocation succeeded, set management data */
134 if (mem_temp != NULL) {
135 size_t i;
136 char *end;
137
138 /* do beginning marker bytes */
139 for (i = 0; i < FENCE_SIZE - sizeof(size_t); i++)
140 *mem_temp++ = 145;
141
142 /* save size in header too */
143 if (memdebug_level > 0)
144 fprintf(trace_file, "*** Requested memory size stored at %p\n", mem_temp);
145 *(size_t *)mem_temp = bytes;
146
147 /* finally, point to storage we are going to hand out */
148 mem_temp += sizeof(size_t);
149
150 /* now, point to trailer bytes and do them */
151 end = mem_temp + bytes;
152 for (i = 0; i < FENCE_SIZE; i++)
153 *end++ = 145;
154
155 /* now zap contents to zero */
156 for (i = 0; i < bytes; i++)
157 mem_temp[i] = 0;
158 }
159
160 /* track pointer if needed */
161 if (memdebug_level > 1 && allocations != NULL) {
162 char key[16];
163 long temp;
164
165 sprintf(key, "%p", mem_temp);
166 if (dict_insert(allocations, key, 1, (const unsigned long) bytes, mem_temp, &temp) == NULL) {
167 fprintf(trace_file, "!!! Insert of pointer tracking info failed\n");
168 abort();
169 }
170 }
171
172 /* allow caller to do error handling */
173 if (memdebug_level > 0) {
174 fprintf(trace_file, "--- Returning pointer of %p\n", mem_temp);
175 fflush(trace_file);
176 }
177 return (void *)mem_temp;
178 }
179
180 /* release some memory, making sure that it was properly allocated */
181 void mem_free(const void *ptr)
182 {
183 char *mem_temp;
184 size_t mem_size;
185 size_t i;
186
187 if (memdebug_level > 0)
188 fprintf(trace_file, "+++ Free of memory located at %p\n", ptr);
189 if (ptr == NULL) {
190 if (memdebug_level > 0) {
191 fprintf(trace_file, "!!! Freeing NULL pointer\n");
192 fflush(trace_file);
193 }
194 abort();
195 }
196
197 mem_validate(ptr); /* doesn't return on error */
198
199 /* get location of size of area */
200 mem_temp = (char *)ptr - sizeof(size_t);
201
202 /* get and calculate real size */
203 mem_size = *(size_t *)mem_temp + (FENCE_SIZE << 1);
204
205 /* if doing memory tracking */
206 if (memdebug_level > 1 && allocations != NULL) {
207 char key[16];
208 STRING_ENTRY *se;
209 long temp;
210
211 sprintf(key, "%p", ptr);
212
213 if ((se = dict_search(allocations, key, &temp)) == NULL) {
214 fprintf(trace_file, "!!! Deleting pointer not found in tracking info\n");
215 abort();
216 }
217
218 if (se->count == 0) {
219 fprintf(trace_file, "!!! Freeing a pointer that has already been freed!\n");
220 abort();
221 }
222 else if (se->flags != mem_size - (FENCE_SIZE << 1)) {
223 fprintf(trace_file, "!!! Stored size different from tracking size!\n");
224 abort();
225 }
226
227 /* remember deleted stuff by zeroing the allocation count */
228 se->count = 0;
229 se->flags = 0;
230 }
231
232 /* zap bytes being freed */
233 for (i = 0, mem_temp = (char *)ptr - FENCE_SIZE; i < mem_size; i++, mem_temp++)
234 *mem_temp = 243;
235
236 if (memdebug_level > 0)
237 fflush(trace_file);
238
239 mem_temp = (char *)ptr - FENCE_SIZE;
240 free((void *)mem_temp);
241 }
242
243 /* reallocate some memory, making sure that it was properly allocated */
244 void *mem_realloc(const void *ptr, const size_t new_size)
245 {
246 char *mem_temp = (char *)ptr;
247 size_t real_size = new_size + (FENCE_SIZE << 1);
248 size_t mem_size;
249 long i;
250
251 if (memdebug_level > 0) {
252 fprintf(trace_file, "+++ Requested size of %ld bytes\n", new_size);
253 fprintf(trace_file, "+++ Actual realloc of %ld bytes located at %p\n", real_size, mem_temp);
254 }
255 if (ptr == NULL) {
256 if (memdebug_level > 0) {
257 fprintf(trace_file, "!!! Reallocating NULL pointer\n");
258 fflush(trace_file);
259 }
260 abort();
261 }
262
263 mem_validate(ptr); /* doesn't return on error */
264
265 /* if doing memory tracking */
266 if (memdebug_level > 1 && allocations != NULL) {
267 char key[16];
268 STRING_ENTRY *se;
269 long temp;
270
271 sprintf(key, "%p", ptr);
272
273 if ((se = dict_search(allocations, key, &temp)) == NULL) {
274 fprintf(trace_file, "!!! Deleting a pointer not found in tracking info!\n");
275 abort();
276 }
277
278 /* point to size bytes */
279 mem_temp = (char *)ptr - sizeof(size_t);
280
281 /* get user size */
282 mem_size = *(size_t *)mem_temp;
283
284 if (se->count == 0) {
285 fprintf(trace_file, "!!! Freeing a pointer that has already been freed!\n");
286 abort();
287 }
288 else if (se->flags != mem_size) {
289 fprintf(trace_file, "!!! Stored size different from tracking size!\n");
290 abort();
291 }
292
293 /* remember deleted stuff by zeroing the allocation count */
294 se->count = 0;
295 se->flags = 0;
296 }
297
298
299 /* header marker bytes will be copied by the realloc */
300 mem_temp = (char *)ptr - FENCE_SIZE;
301 mem_temp = realloc((void *)mem_temp, real_size);
302
303 if (mem_temp != NULL) {
304 char *end;
305
306 /* save size in header too */
307 mem_temp += FENCE_SIZE - sizeof(size_t);
308 if (memdebug_level > 0)
309 fprintf(trace_file, "*** Requested memory size stored at %p\n", mem_temp);
310 *(size_t *)mem_temp = new_size;
311
312 /* finally, point to storage we are going to hand out */
313 mem_temp += sizeof(size_t);
314
315 /* now, point to trailer bytes and do them */
316 end = mem_temp + new_size;
317 for (i = 0; i < FENCE_SIZE; i++)
318 *end++ = 145;
319 }
320
321 if (memdebug_level > 1 && allocations != NULL) {
322 char key[16];
323 long temp;
324
325 sprintf(key, "%p", mem_temp);
326 if (dict_insert(allocations, key, 1, (const unsigned long)new_size, mem_temp, &temp) == NULL) {
327 fprintf(trace_file, "!!! Insert of pointer tracking info failed\n");
328 abort();
329 }
330 }
331
332 if (memdebug_level > 0) {
333 fprintf(trace_file, "--- Returning pointer of %p\n", mem_temp);
334 fflush(trace_file);
335 }
336 return (void *)mem_temp;
337 }
338
339 /* check a pointer to be sure all check bytes are OK. abort if not */
340 /* always returns 1 if not aborting so that it can be included in */
341 /* if statement boolean expressions */
342 int mem_validate(void *ptr)
343 {
344 unsigned char *mem_temp = (unsigned char *)ptr;
345 size_t mem_size;
346 size_t i;
347
348 /* NULL pointers are always valid */
349 if (ptr == NULL)
350 return 1;
351
352 if (memdebug_level > 0)
353 fprintf(trace_file, "+++ Checking %p as pointer\n", ptr);
354
355
356 if (memdebug_level > 1 && allocations != NULL) {
357 char key[16];
358 STRING_ENTRY *se;
359 long temp;
360
361 sprintf(key, "%p", ptr);
362
363 if ((se = dict_search(allocations, key, &temp)) == NULL) {
364 fprintf(trace_file, "!!! Pointer not found in tracking info!\n");
365 abort();
366 }
367
368 /* point to size bytes */
369 mem_temp = (unsigned char *)ptr - sizeof(size_t);
370
371 /* get user size */
372 mem_size = *(size_t *)mem_temp;
373
374 if (se->count == 0) {
375 fprintf(trace_file, "!!! Checking pointer has been freed!\n");
376 abort();
377 }
378 else if (se->flags != mem_size) {
379 fprintf(trace_file, "!!! Stored size different from tracking size!\n");
380 abort();
381 }
382 }
383
384 /* check the header bytes */
385 mem_temp = (unsigned char *) ptr - FENCE_SIZE;
386 if (memdebug_level > 0)
387 fprintf(trace_file, "+++ Real pointer at %p\n", mem_temp);
388
389
390 for (i = 0; i < FENCE_SIZE - sizeof(size_t); i++)
391 if (*mem_temp++ != 145) {
392 if (memdebug_level > 0) {
393 fprintf(trace_file, "!!! The user pointer at %p has been overwritten\n", ptr);
394 fprintf(trace_file, "!!! Header offset %ld has been changed\n", i - 1);
395 fflush(trace_file);
396 }
397 abort();
398 }
399
400 /* check size */
401 i = *(size_t *)mem_temp;
402 if (memdebug_level > 0)
403 fprintf(trace_file, "*** Stored memory size of %ld bytes in header\n", i);
404
405
406 /* now point to where trailer should be */
407 mem_temp = (unsigned char *)ptr + i;
408 for (i = 0; i < FENCE_SIZE; i++)
409 if (*mem_temp++ != 145) {
410 if (memdebug_level > 0) {
411 fprintf(trace_file, "!!! The user pointer at %p has been overwritten\n", ptr);
412 fprintf(trace_file, "!!! Trailer offset %ld has been changed\n", i - 1);
413 fflush(trace_file);
414 }
415 abort();
416 }
417 if (memdebug_level > 0)
418 fflush(trace_file);
419 return 1;
420 }