Mercurial > hg > early-roguelike
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; | |