Mac intel commit
[blender.git] / intern / guardedalloc / intern / mallocn.c
1 /**
2  * $Id$
3  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version. The Blender
9  * Foundation also sells licenses for use in proprietary software under
10  * the Blender License.  See http://www.blender.org/BL/ for information
11  * about this.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30  */
31
32 /**
33
34  * $Id$
35  * Copyright (C) 2001 NaN Technologies B.V.
36  * Guarded memory allocation, and boundary-write detection.
37  */
38
39 #include <stdlib.h>
40 #include <string.h>     /* memcpy */
41 #include <stdarg.h>
42
43 /* mmap exception */
44 #if defined(AMIGA) || defined(__BeOS) || defined(WIN32)
45 #else
46 #include <sys/types.h>
47 #include <sys/mman.h>
48 #endif
49
50 #include "MEM_guardedalloc.h"
51
52 /* --------------------------------------------------------------------- */
53 /* Data definition                                                       */
54 /* --------------------------------------------------------------------- */
55 /* all memory chunks are put in linked lists */
56 typedef struct localLink
57 {
58         struct localLink *next,*prev;
59 } localLink;
60
61 typedef struct localListBase 
62 {
63         void *first, *last;
64 } localListBase;
65
66         /* note: keep this struct aligned (e.g., irix/gcc) - Hos */
67 typedef struct MemHead {
68         int tag1;
69         int len;
70         struct MemHead *next,*prev;
71         const char * name;
72         const char * nextname;
73         int tag2;
74         int mmap;       /* if true, memory was mmapped */
75 } MemHead;
76
77 typedef struct MemTail {
78         int tag3, pad;
79 } MemTail;
80
81
82 /* --------------------------------------------------------------------- */
83 /* local functions                                                       */
84 /* --------------------------------------------------------------------- */
85
86 static void addtail(volatile localListBase *listbase, void *vlink);
87 static void remlink(volatile localListBase *listbase, void *vlink);
88 static void rem_memblock(MemHead *memh);
89 static void MemorY_ErroR(const char *block, const char *error);
90 static const char *check_memlist(MemHead *memh);
91
92 /* --------------------------------------------------------------------- */
93 /* locally used defines                                                  */
94 /* --------------------------------------------------------------------- */
95
96 #if defined( __sgi) || defined (__sun) || defined (__sun__) || defined (__sparc) || defined (__sparc__) || defined (__PPC__) || (defined (__APPLE__) && !defined(__LITTLE_ENDIAN__))
97 #define MAKE_ID(a,b,c,d) ( (int)(a)<<24 | (int)(b)<<16 | (c)<<8 | (d) )
98 #else
99 #define MAKE_ID(a,b,c,d) ( (int)(d)<<24 | (int)(c)<<16 | (b)<<8 | (a) )
100 #endif
101
102 #define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
103 #define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
104 #define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
105 #define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
106
107 #define MEMNEXT(x) ((MemHead *)(((char *) x) - ((char *) & (((MemHead *)0)->next))))
108         
109 /* --------------------------------------------------------------------- */
110 /* vars                                                                  */
111 /* --------------------------------------------------------------------- */
112         
113
114 volatile int totblock= 0;
115 volatile unsigned long mem_in_use= 0, mmap_in_use= 0;
116
117 volatile static struct localListBase _membase;
118 volatile static struct localListBase *membase = &_membase;
119 static void (*error_callback)(char *) = NULL;
120
121 #ifdef malloc
122 #undef malloc
123 #endif
124
125 #ifdef calloc
126 #undef calloc
127 #endif
128
129 #ifdef free
130 #undef free
131 #endif
132
133
134 /* --------------------------------------------------------------------- */
135 /* implementation                                                        */
136 /* --------------------------------------------------------------------- */
137
138 static void print_error(const char *str, ...)
139 {
140         char buf[1024];
141         va_list ap;
142
143         va_start(ap, str);
144         vsprintf(buf, str, ap);
145         va_end(ap);
146
147         if (error_callback) error_callback(buf);
148 }
149
150 int MEM_check_memory_integrity()
151 {
152         const char* err_val = NULL;
153         MemHead* listend;
154         /* check_memlist starts from the front, and runs until it finds
155          * the requested chunk. For this test, that's the last one. */
156         listend = membase->last;
157         
158         err_val = check_memlist(listend);
159
160         if (err_val == 0) return 0;
161         return 1;
162 }
163
164
165 void MEM_set_error_callback(void (*func)(char *))
166 {
167         error_callback = func;
168 }
169
170
171 int MEM_allocN_len(void *vmemh)
172 {
173         if (vmemh) {
174                 MemHead *memh= vmemh;
175         
176                 memh--;
177                 return memh->len;
178         } else
179                 return 0;
180 }
181
182 void *MEM_dupallocN(void *vmemh)
183 {
184         void *newp= NULL;
185         
186         if (vmemh) {
187                 MemHead *memh= vmemh;
188                 memh--;
189                 
190                 if(memh->mmap)
191                         newp= MEM_mapallocN(memh->len, "dupli_alloc");
192                 else
193                         newp= MEM_mallocN(memh->len, "dupli_mapalloc");
194                 memcpy(newp, vmemh, memh->len);
195         }
196
197         return newp;
198 }
199
200 static void make_memhead_header(MemHead *memh, unsigned int len, const char *str)
201 {
202         MemTail *memt;
203         
204         memh->tag1 = MEMTAG1;
205         memh->name = str;
206         memh->nextname = 0;
207         memh->len = len;
208         memh->mmap = 0;
209         memh->tag2 = MEMTAG2;
210         
211         memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
212         memt->tag3 = MEMTAG3;
213         
214         addtail(membase,&memh->next);
215         if (memh->next) memh->nextname = MEMNEXT(memh->next)->name;
216         
217         totblock++;
218         mem_in_use += len;
219 }
220
221 void *MEM_mallocN(unsigned int len, const char *str)
222 {
223         MemHead *memh;
224
225         len = (len + 3 ) & ~3;  /* allocate in units of 4 */
226         
227         memh= (MemHead *)malloc(len+sizeof(MemHead)+sizeof(MemTail));
228
229         if(memh) {
230                 make_memhead_header(memh, len, str);
231                 return (++memh);
232         }
233         print_error("Malloc returns nill: len=%d in %s, total %u\n",len, str, mem_in_use);
234         return NULL;
235 }
236
237 void *MEM_callocN(unsigned int len, const char *str)
238 {
239         MemHead *memh;
240
241         len = (len + 3 ) & ~3;  /* allocate in units of 4 */
242
243         memh= (MemHead *)calloc(len+sizeof(MemHead)+sizeof(MemTail),1);
244
245         if(memh) {
246                 make_memhead_header(memh, len, str);
247                 return (++memh);
248         }
249         print_error("Calloc returns nill: len=%d in %s, total %u\n",len, str, mem_in_use);
250         return 0;
251 }
252
253 /* note; mmap returns zero'd memory */
254 void *MEM_mapallocN(unsigned int len, const char *str)
255 {
256 #if defined(AMIGA) || defined(__BeOS) || defined(WIN32)
257         return MEM_callocN(len, str);
258 #else
259         MemHead *memh;
260         
261         len = (len + 3 ) & ~3;  /* allocate in units of 4 */
262         
263 #ifdef __sgi
264         {
265 #include <fcntl.h>
266
267           int fd;
268           fd = open("/dev/zero", O_RDWR);
269
270           memh= mmap(0, len+sizeof(MemHead)+sizeof(MemTail),
271                      PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
272           close(fd);
273         }
274 #else
275         memh= mmap(0, len+sizeof(MemHead)+sizeof(MemTail),
276                    PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
277 #endif
278
279         if(memh!=(MemHead *)-1) {
280                 make_memhead_header(memh, len, str);
281                 memh->mmap= 1;
282                 mmap_in_use += len;
283                 return (++memh);
284         }
285         else {
286                 print_error("Mapalloc returns nill, fallback to regular malloc: len=%d in %s, total %u\n",len, str, mmap_in_use);
287                 return MEM_callocN(len, str);
288         }
289         return NULL;
290 #endif
291 }
292
293
294 void MEM_printmemlist()
295 {
296         MemHead *membl;
297
298         membl = membase->first;
299         if (membl) membl = MEMNEXT(membl);
300         while(membl) {
301                 print_error("%s len: %d %p\n",membl->name,membl->len, membl+1);
302                 if(membl->next)
303                         membl= MEMNEXT(membl->next);
304                 else break;
305         }
306 }
307
308 short MEM_freeN(void *vmemh)            /* anders compileertie niet meer */
309 {
310         short error = 0;
311         MemTail *memt;
312         MemHead *memh= vmemh;
313         const char *name;
314
315         if (memh == NULL){
316                 MemorY_ErroR("free","attempt to free NULL pointer");
317                 /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
318                 return(-1);
319         }
320
321         if(sizeof(long)==8) {
322                 if (((long) memh) & 0x7) {
323                         MemorY_ErroR("free","attempt to free illegal pointer");
324                         return(-1);
325                 }
326         }
327         else {
328                 if (((long) memh) & 0x3) {
329                         MemorY_ErroR("free","attempt to free illegal pointer");
330                         return(-1);
331                 }
332         }
333         
334         memh--;
335         if(memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
336                 MemorY_ErroR(memh->name,"double free");
337                 return(-1);
338         }
339
340         if ((memh->tag1 == MEMTAG1) && (memh->tag2 == MEMTAG2) && ((memh->len & 0x3) == 0)) {
341                 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
342                 if (memt->tag3 == MEMTAG3){
343                         
344                         memh->tag1 = MEMFREE;
345                         memh->tag2 = MEMFREE;
346                         memt->tag3 = MEMFREE;
347                         /* after tags !!! */
348                         rem_memblock(memh);
349                         
350                         return(0);
351                 }
352                 error = 2;
353                 MemorY_ErroR(memh->name,"end corrupt");
354                 name = check_memlist(memh);
355                 if (name != 0){
356                         if (name != memh->name) MemorY_ErroR(name,"is also corrupt");
357                 }
358         } else{
359                 error = -1;
360                 name = check_memlist(memh);
361                 if (name == 0) MemorY_ErroR("free","pointer not in memlist");
362                 else MemorY_ErroR(name,"error in header");
363         }
364
365         totblock--;
366         /* here a DUMP should happen */
367
368         return(error);
369 }
370
371 /* --------------------------------------------------------------------- */
372 /* local functions                                                       */
373 /* --------------------------------------------------------------------- */
374
375 static void addtail(volatile localListBase *listbase, void *vlink)
376 {
377         struct localLink *link= vlink;
378
379         if (link == 0) return;
380         if (listbase == 0) return;
381
382         link->next = 0;
383         link->prev = listbase->last;
384
385         if (listbase->last) ((struct localLink *)listbase->last)->next = link;
386         if (listbase->first == 0) listbase->first = link;
387         listbase->last = link;
388 }
389
390 static void remlink(volatile localListBase *listbase, void *vlink)
391 {
392         struct localLink *link= vlink;
393
394         if (link == 0) return;
395         if (listbase == 0) return;
396
397         if (link->next) link->next->prev = link->prev;
398         if (link->prev) link->prev->next = link->next;
399
400         if (listbase->last == link) listbase->last = link->prev;
401         if (listbase->first == link) listbase->first = link->next;
402 }
403
404 static void rem_memblock(MemHead *memh)
405 {
406     remlink(membase,&memh->next);
407     if (memh->prev){
408         if (memh->next) MEMNEXT(memh->prev)->nextname = 
409 MEMNEXT(memh->next)->name;
410         else MEMNEXT(memh->prev)->nextname = 0;
411     }
412
413     totblock--;
414     mem_in_use -= memh->len;
415    
416 #if defined(AMIGA) || defined(__BeOS) || defined(WIN32)
417     free(memh);
418 #else   
419    
420     if(memh->mmap) {
421         mmap_in_use -= memh->len;
422         if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
423             printf("Couldn't unmap memory %s\n", memh->name);
424     }
425     else   
426         free(memh);
427 #endif
428 }
429
430 static void MemorY_ErroR(const char *block, const char *error)
431 {
432         print_error("Memoryblock %s: %s\n",block, error);
433 }
434
435 static const char *check_memlist(MemHead *memh)
436 {
437         MemHead *forw,*back,*forwok,*backok;
438         const char *name;
439
440         forw = membase->first;
441         if (forw) forw = MEMNEXT(forw);
442         forwok = 0;
443         while(forw){
444                 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
445                 forwok = forw;
446                 if (forw->next) forw = MEMNEXT(forw->next);
447                 else forw = 0;
448         }
449
450         back = (MemHead *) membase->last;
451         if (back) back = MEMNEXT(back);
452         backok = 0;
453         while(back){
454                 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
455                 backok = back;
456                 if (back->prev) back = MEMNEXT(back->prev);
457                 else back = 0;
458         }
459
460         if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
461
462         if (forw == 0 && back == 0){
463                 /* geen foute headers gevonden dan maar op zoek naar memblock*/
464
465                 forw = membase->first;
466                 if (forw) forw = MEMNEXT(forw);
467                 forwok = 0;
468                 while(forw){
469                         if (forw == memh) break;
470                         if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
471                         forwok = forw;
472                         if (forw->next) forw = MEMNEXT(forw->next);
473                         else forw = 0;
474                 }
475                 if (forw == 0) return (0);
476
477                 back = (MemHead *) membase->last;
478                 if (back) back = MEMNEXT(back);
479                 backok = 0;
480                 while(back){
481                         if (back == memh) break;
482                         if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
483                         backok = back;
484                         if (back->prev) back = MEMNEXT(back->prev);
485                         else back = 0;
486                 }
487         }
488
489         if (forwok) name = forwok->nextname;
490         else name = "No name found";
491
492         if (forw == memh){
493                 /* voor alle zekerheid wordt dit block maar uit de lijst gehaald */
494                 if (forwok){
495                         if (backok){
496                                 forwok->next = (MemHead *)&backok->next;
497                                 backok->prev = (MemHead *)&forwok->next;
498                                 forwok->nextname = backok->name;
499                         } else{
500                                 forwok->next = 0;
501                                 membase->last = (struct localLink *) &forwok->next; 
502 /*                              membase->last = (struct Link *) &forwok->next; */
503                         }
504                 } else{
505                         if (backok){
506                                 backok->prev = 0;
507                                 membase->first = &backok->next;
508                         } else{
509                                 membase->first = membase->last = 0;
510                         }
511                 }
512         } else{
513                 MemorY_ErroR(name,"Additional error in header");
514                 return("Additional error in header");
515         }
516
517         return(name);
518 }
519
520 /* eof */