Commit | Line | Data |
---|---|---|
1bbcbbe4 TC |
1 | #include "alloc.h" |
2 | #include <stdio.h> | |
3 | #include <errno.h> | |
4 | #include <stdarg.h> | |
e0b3527a | 5 | #include <string.h> |
1bbcbbe4 TC |
6 | |
7 | /* cheap version of Imager's logging */ | |
8 | char const *last_file; int last_line; | |
9 | ||
10 | static void do_log(int level, char const *fmt, ...); | |
800342e1 TC |
11 | |
12 | #define mm_log(x) ((last_file = __FILE__), (last_line = __LINE__), do_log x ) | |
13 | ||
14 | #ifdef MEM_DEBUG | |
15 | ||
1bbcbbe4 TC |
16 | static |
17 | void | |
18 | bndcheck(int idx); | |
19 | void | |
20 | bndcheck_all(void); | |
21 | ||
1bbcbbe4 TC |
22 | #define MAXMAL 102400 |
23 | #define MAXDESC 65 | |
24 | ||
25 | #define UNDRRNVAL 10 | |
26 | #define OVERRNVAL 10 | |
27 | ||
28 | #define PADBYTE 0xaa | |
29 | ||
30 | static int malloc_need_init = 1; | |
31 | ||
32 | void | |
33 | malloc_state(void); | |
34 | ||
35 | typedef struct { | |
36 | void* ptr; | |
37 | size_t size; | |
38 | char comm[MAXDESC]; | |
39 | } malloc_entry; | |
40 | ||
41 | malloc_entry malloc_pointers[MAXMAL]; | |
42 | ||
43 | ||
44 | ||
45 | /* Utility functions */ | |
46 | ||
47 | ||
48 | static | |
49 | void | |
50 | malloc_init(void) { | |
51 | int i; | |
52 | for(i=0; i<MAXMAL; i++) malloc_pointers[i].ptr = NULL; | |
53 | malloc_need_init = 0; | |
54 | atexit(malloc_state); | |
55 | } | |
56 | ||
57 | ||
58 | static | |
59 | int | |
60 | find_ptr(void *p) { | |
61 | int i; | |
62 | for(i=0;i<MAXMAL;i++) | |
63 | if (malloc_pointers[i].ptr == p) | |
64 | return i; | |
65 | return -1; | |
66 | } | |
67 | ||
68 | ||
69 | /* Takes a pointer to real start of array, | |
70 | * sets the entries in the table, returns | |
71 | * the offset corrected pointer */ | |
72 | ||
73 | static | |
74 | void * | |
75 | set_entry(int i, char *buf, size_t size, char const *file, int line) { | |
76 | memset( buf, PADBYTE, UNDRRNVAL ); | |
77 | memset( &buf[UNDRRNVAL+size], PADBYTE, OVERRNVAL ); | |
78 | buf += UNDRRNVAL; | |
79 | malloc_pointers[i].ptr = buf; | |
80 | malloc_pointers[i].size = size; | |
81 | sprintf(malloc_pointers[i].comm,"%s (%d)", file, line); | |
82 | return buf; | |
83 | } | |
84 | ||
85 | void | |
86 | malloc_state(void) { | |
87 | int i, total = 0; | |
88 | ||
89 | mm_log((0,"malloc_state()\n")); | |
90 | bndcheck_all(); | |
91 | for(i=0; i<MAXMAL; i++) if (malloc_pointers[i].ptr != NULL) { | |
92 | mm_log((0,"%d: %d (0x%x) : %s\n", i, malloc_pointers[i].size, malloc_pointers[i].ptr, malloc_pointers[i].comm)); | |
93 | total += malloc_pointers[i].size; | |
94 | } | |
95 | if (total == 0) mm_log((0,"No memory currently used!\n")); | |
96 | else mm_log((0,"total: %d\n",total)); | |
97 | } | |
98 | ||
99 | ||
100 | ||
101 | void* | |
102 | mymalloc_file_line(size_t size, char const* file, int line) { | |
103 | char *buf; | |
104 | int i; | |
105 | if (malloc_need_init) malloc_init(); | |
106 | ||
107 | /* bndcheck_all(); Uncomment for LOTS OF THRASHING */ | |
108 | ||
109 | if ( (i = find_ptr(NULL)) < 0 ) { | |
110 | mm_log((0,"more than %d segments allocated at %s (%d)\n", MAXMAL, file, line)); | |
111 | exit(3); | |
112 | } | |
113 | ||
114 | if ( (buf = malloc(size+UNDRRNVAL+OVERRNVAL)) == NULL ) { | |
115 | mm_log((1,"Unable to allocate %i for %s (%i)\n", size, file, line)); | |
116 | exit(3); | |
117 | } | |
118 | ||
119 | buf = set_entry(i, buf, size, file, line); | |
120 | mm_log((1,"mymalloc_file_line: slot <%d> %d bytes allocated at %p for %s (%d)\n", i, size, buf, file, line)); | |
121 | return buf; | |
122 | } | |
123 | ||
124 | void * | |
125 | (mymalloc)(int size) { | |
126 | return mymalloc_file_line(size, "unknown", 0); | |
127 | } | |
128 | ||
129 | void* | |
130 | myrealloc_file_line(void *ptr, size_t newsize, char const * file, int line) { | |
131 | char *buf; | |
132 | int i; | |
133 | ||
134 | if (malloc_need_init) malloc_init(); | |
135 | /* bndcheck_all(); ACTIVATE FOR LOTS OF THRASHING */ | |
136 | ||
137 | if (!ptr) { | |
138 | mm_log((1, "realloc called with ptr = NULL, sending request to malloc\n")); | |
139 | return mymalloc_file_line(newsize, file, line); | |
140 | } | |
141 | ||
142 | if (!newsize) { | |
143 | mm_log((1, "newsize = 0, sending request to free\n")); | |
144 | myfree_file_line(ptr, file, line); | |
145 | return NULL; | |
146 | } | |
147 | ||
148 | if ( (i = find_ptr(ptr)) == -1) { | |
149 | mm_log((0, "Unable to find %p in realloc for %s (%i)\n", ptr, file, line)); | |
150 | exit(3); | |
151 | } | |
152 | ||
153 | if ( (buf = realloc(((char *)ptr)-UNDRRNVAL, UNDRRNVAL+OVERRNVAL+newsize)) == NULL ) { | |
154 | mm_log((1,"Unable to reallocate %i bytes at %p for %s (%i)\n", newsize, ptr, file, line)); | |
155 | exit(3); | |
156 | } | |
157 | ||
158 | buf = set_entry(i, buf, newsize, file, line); | |
159 | mm_log((1,"realloc_file_line: slot <%d> %d bytes allocated at %p for %s (%d)\n", i, newsize, buf, file, line)); | |
160 | return buf; | |
161 | } | |
162 | ||
163 | void * | |
164 | (myrealloc)(void *ptr, size_t newsize) { | |
165 | return myrealloc_file_line(ptr, newsize, "unknown", 0); | |
166 | } | |
167 | ||
168 | static | |
169 | void | |
170 | bndcheck(int idx) { | |
171 | int i; | |
172 | size_t s = malloc_pointers[idx].size; | |
173 | unsigned char *pp = malloc_pointers[idx].ptr; | |
174 | if (!pp) { | |
175 | mm_log((1, "bndcheck: No pointer in slot %d\n", idx)); | |
176 | return; | |
177 | } | |
178 | ||
179 | for(i=0;i<UNDRRNVAL;i++) | |
180 | if (pp[-(1+i)] != PADBYTE) | |
181 | mm_log((1,"bndcheck: UNDERRUN OF %d bytes detected: slot = %d, point = %p, size = %d\n", i+1, idx, pp, s )); | |
182 | ||
183 | for(i=0;i<OVERRNVAL;i++) | |
184 | if (pp[s+i] != PADBYTE) | |
185 | mm_log((1,"bndcheck: OVERRUN OF %d bytes detected: slot = %d, point = %p, size = %d\n", i+1, idx, pp, s )); | |
186 | } | |
187 | ||
188 | void | |
189 | bndcheck_all(void) { | |
190 | int idx; | |
191 | mm_log((1, "bndcheck_all()\n")); | |
192 | for(idx=0; idx<MAXMAL; idx++) | |
193 | if (malloc_pointers[idx].ptr) | |
194 | bndcheck(idx); | |
195 | } | |
196 | ||
197 | void | |
198 | myfree_file_line(void *p, char const *file, int line) { | |
199 | char *pp = p; | |
200 | int match = 0; | |
201 | int i; | |
202 | ||
203 | for(i=0; i<MAXMAL; i++) if (malloc_pointers[i].ptr == p) { | |
204 | mm_log((1,"myfree_file_line: pointer %i (%s) freed at %s (%i)\n", i, malloc_pointers[i].comm, file, line)); | |
205 | bndcheck(i); | |
206 | malloc_pointers[i].ptr = NULL; | |
207 | match++; | |
208 | } | |
209 | ||
210 | mm_log((1, "myfree_file_line: freeing address %p (real %p) (%s:%d)\n", pp, pp-UNDRRNVAL, file, line)); | |
211 | ||
212 | if (match != 1) { | |
213 | mm_log((1, "myfree_file_line: INCONSISTENT REFCOUNT %d at %s (%i)\n", match, file, line)); | |
214 | fprintf(stderr, "myfree_file_line: INCONSISTENT REFCOUNT %d at %s (%i)\n", match, file, line); | |
215 | exit(255); | |
216 | } | |
217 | ||
218 | ||
219 | free(pp-UNDRRNVAL); | |
220 | } | |
221 | ||
222 | void | |
223 | (myfree)(void *block) { | |
224 | myfree_file_line(block, "unknown", 0); | |
225 | } | |
226 | ||
227 | #else | |
228 | ||
229 | #define malloc_comm(a,b) (mymalloc(a)) | |
230 | ||
231 | void | |
232 | malloc_state() { | |
233 | } | |
234 | ||
235 | void* | |
236 | mymalloc(size_t size) { | |
237 | void *buf; | |
238 | ||
239 | if (size < 0) { | |
70aaf253 | 240 | fprintf(stderr, "Attempt to allocate size %d\n", (int)size); |
1bbcbbe4 TC |
241 | exit(3); |
242 | } | |
243 | ||
244 | if ( (buf = malloc(size)) == NULL ) { | |
70aaf253 TC |
245 | mm_log((1, "mymalloc: unable to malloc %d\n", (int)size)); |
246 | fprintf(stderr,"Unable to malloc %d.\n", (int)size); exit(3); | |
1bbcbbe4 | 247 | } |
70aaf253 | 248 | mm_log((1, "mymalloc(size %d) -> %p\n", (int)size, buf)); |
1bbcbbe4 TC |
249 | return buf; |
250 | } | |
251 | ||
252 | void * | |
253 | mymalloc_file_line(size_t size, char *file, int line) { | |
254 | return mymalloc(size); | |
255 | } | |
256 | ||
257 | void | |
258 | myfree(void *p) { | |
259 | mm_log((1, "myfree(p %p)\n", p)); | |
260 | free(p); | |
261 | } | |
262 | ||
263 | void | |
264 | myfree_file_line(void *p, char *file, int line) { | |
265 | myfree(p); | |
266 | } | |
267 | ||
268 | void * | |
269 | myrealloc(void *block, size_t size) { | |
270 | void *result; | |
271 | ||
272 | mm_log((1, "myrealloc(block %p, size %u)\n", block, size)); | |
273 | if ((result = realloc(block, size)) == NULL) { | |
274 | mm_log((1, "myrealloc: out of memory\n")); | |
275 | fprintf(stderr, "Out of memory.\n"); | |
276 | exit(3); | |
277 | } | |
278 | return result; | |
279 | } | |
280 | ||
281 | void * | |
282 | myrealloc_file_line(void *block, size_t newsize, char *file, int size) { | |
283 | return myrealloc(block, newsize); | |
284 | } | |
285 | ||
286 | #endif /* IMAGER_MALLOC_DEBUG */ | |
287 | ||
288 | FILE *log_file; | |
289 | ||
290 | void | |
291 | setup_log(void) { | |
292 | char *log_env = getenv("MEM_DEBUG"); | |
293 | if (!log_env) | |
294 | return; | |
295 | ||
296 | if (strcmp(log_env, "STDERR") == 0) { | |
297 | log_file = stderr; | |
298 | return; | |
299 | } | |
300 | log_file = fopen(log_env, "w+"); | |
301 | if (!log_file) { | |
302 | fprintf(stderr, "Could not open log %s: %s\n", log_env, strerror(errno)); | |
303 | exit(3); | |
304 | } | |
305 | } | |
306 | ||
307 | static void | |
308 | do_log(int level, char const *fmt, ...) { | |
309 | if (!log_file) setup_log(); | |
310 | ||
311 | if (log_file) { | |
312 | va_list ap; | |
313 | va_start(ap, fmt); | |
314 | fprintf(log_file, "[%s:%d] %d:", last_file, last_line, level); | |
315 | ||
316 | vfprintf(stderr, fmt, ap); | |
317 | va_end(ap); | |
318 | } | |
319 | } |