Ambient occlusion baker from multi-resolution mesh
[blender.git] / intern / guardedalloc / intern / mallocn.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Brecht Van Lommel
24  *                 Campbell Barton
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /** \file guardedalloc/intern/mallocn.c
30  *  \ingroup MEM
31  *
32  * Guarded memory allocation, and boundary-write detection.
33  */
34
35 #include <stdlib.h>
36 #include <string.h> /* memcpy */
37 #include <stdarg.h>
38 #include <sys/types.h>
39 /* Blame Microsoft for LLP64 and no inttypes.h, quick workaround needed: */
40 #if defined(WIN64)
41 #  define SIZET_FORMAT "%I64u"
42 #  define SIZET_ARG(a) ((unsigned long long)(a))
43 #else
44 #  define SIZET_FORMAT "%lu"
45 #  define SIZET_ARG(a) ((unsigned long)(a))
46 #endif
47
48 /* mmap exception */
49 #if defined(WIN32)
50 #  include "mmap_win.h"
51 #else
52 #  include <sys/mman.h>
53 #endif
54
55 #include "MEM_guardedalloc.h"
56
57 /* Only for debugging:
58  * store original buffer's name when doing MEM_dupallocN
59  * helpful to profile issues with non-freed "dup_alloc" buffers,
60  * but this introduces some overhead to memory header and makes
61  * things slower a bit, so better to keep disabled by default
62  */
63 //#define DEBUG_MEMDUPLINAME
64
65 /* Only for debugging:
66  * lets you count the allocations so as to find the allocator of unfreed memory
67  * in situations where the leak is predictable */
68
69 //#define DEBUG_MEMCOUNTER
70
71 #ifdef DEBUG_MEMCOUNTER
72    /* set this to the value that isn't being freed */
73 #  define DEBUG_MEMCOUNTER_ERROR_VAL 0
74 static int _mallocn_count = 0;
75
76 /* breakpoint here */
77 static void memcount_raise(const char *name)
78 {
79         fprintf(stderr, "%s: memcount-leak, %d\n", name, _mallocn_count);
80 }
81 #endif
82
83 /* --------------------------------------------------------------------- */
84 /* Data definition                                                       */
85 /* --------------------------------------------------------------------- */
86 /* all memory chunks are put in linked lists */
87 typedef struct localLink {
88         struct localLink *next, *prev;
89 } localLink;
90
91 typedef struct localListBase {
92         void *first, *last;
93 } localListBase;
94
95 /* note: keep this struct aligned (e.g., irix/gcc) - Hos */
96 typedef struct MemHead {
97         int tag1;
98         size_t len;
99         struct MemHead *next, *prev;
100         const char *name;
101         const char *nextname;
102         int tag2;
103         int mmap;  /* if true, memory was mmapped */
104 #ifdef DEBUG_MEMCOUNTER
105         int _count;
106 #endif
107
108 #ifdef DEBUG_MEMDUPLINAME
109         int need_free_name, pad;
110 #endif
111 } MemHead;
112
113 /* for openmp threading asserts, saves time troubleshooting
114  * we may need to extend this if blender code starts using MEM_
115  * functions inside OpenMP correctly with omp_set_lock() */
116
117 #if 0  /* disable for now, only use to debug openmp code which doesn lock threads for malloc */
118 #if defined(_OPENMP) && defined(DEBUG)
119 #  include <assert.h>
120 #  include <omp.h>
121 #  define DEBUG_OMP_MALLOC
122 #endif
123 #endif
124
125 typedef struct MemTail {
126         int tag3, pad;
127 } MemTail;
128
129
130 /* --------------------------------------------------------------------- */
131 /* local functions                                                       */
132 /* --------------------------------------------------------------------- */
133
134 static void addtail(volatile localListBase *listbase, void *vlink);
135 static void remlink(volatile localListBase *listbase, void *vlink);
136 static void rem_memblock(MemHead *memh);
137 static void MemorY_ErroR(const char *block, const char *error);
138 static const char *check_memlist(MemHead *memh);
139
140 /* --------------------------------------------------------------------- */
141 /* locally used defines                                                  */
142 /* --------------------------------------------------------------------- */
143
144 #ifdef __BIG_ENDIAN__
145 #  define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d))
146 #else
147 #  define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
148 #endif
149
150 #define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
151 #define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
152 #define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
153 #define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
154
155 #define MEMNEXT(x) \
156         ((MemHead *)(((char *) x) - ((char *) &(((MemHead *)0)->next))))
157         
158 /* --------------------------------------------------------------------- */
159 /* vars                                                                  */
160 /* --------------------------------------------------------------------- */
161         
162
163 static volatile int totblock = 0;
164 static volatile uintptr_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
165
166 static volatile struct localListBase _membase;
167 static volatile struct localListBase *membase = &_membase;
168 static void (*error_callback)(const char *) = NULL;
169 static void (*thread_lock_callback)(void) = NULL;
170 static void (*thread_unlock_callback)(void) = NULL;
171
172 static int malloc_debug_memset = 0;
173
174 #ifdef malloc
175 #undef malloc
176 #endif
177
178 #ifdef calloc
179 #undef calloc
180 #endif
181
182 #ifdef free
183 #undef free
184 #endif
185
186
187 /* --------------------------------------------------------------------- */
188 /* implementation                                                        */
189 /* --------------------------------------------------------------------- */
190
191 #ifdef __GNUC__
192 __attribute__ ((format(printf, 1, 2)))
193 #endif
194 static void print_error(const char *str, ...)
195 {
196         char buf[512];
197         va_list ap;
198
199         va_start(ap, str);
200         vsnprintf(buf, sizeof(buf), str, ap);
201         va_end(ap);
202         buf[sizeof(buf) - 1] = '\0';
203
204         if (error_callback) error_callback(buf);
205 }
206
207 static void mem_lock_thread(void)
208 {
209 #ifdef DEBUG_OMP_MALLOC
210         assert(omp_in_parallel() == 0);
211 #endif
212
213         if (thread_lock_callback)
214                 thread_lock_callback();
215 }
216
217 static void mem_unlock_thread(void)
218 {
219         if (thread_unlock_callback)
220                 thread_unlock_callback();
221 }
222
223 int MEM_check_memory_integrity(void)
224 {
225         const char *err_val = NULL;
226         MemHead *listend;
227         /* check_memlist starts from the front, and runs until it finds
228          * the requested chunk. For this test, that's the last one. */
229         listend = membase->last;
230         
231         err_val = check_memlist(listend);
232
233         return (err_val != NULL);
234 }
235
236
237 void MEM_set_error_callback(void (*func)(const char *))
238 {
239         error_callback = func;
240 }
241
242 void MEM_set_lock_callback(void (*lock)(void), void (*unlock)(void))
243 {
244         thread_lock_callback = lock;
245         thread_unlock_callback = unlock;
246 }
247
248 void MEM_set_memory_debug(void)
249 {
250         malloc_debug_memset = 1;
251 }
252
253 size_t MEM_allocN_len(const void *vmemh)
254 {
255         if (vmemh) {
256                 const MemHead *memh = vmemh;
257         
258                 memh--;
259                 return memh->len;
260         }
261         else {
262                 return 0;
263         }
264 }
265
266 void *MEM_dupallocN(const void *vmemh)
267 {
268         void *newp = NULL;
269         
270         if (vmemh) {
271                 const MemHead *memh = vmemh;
272                 memh--;
273
274 #ifndef DEBUG_MEMDUPLINAME
275                 if (memh->mmap)
276                         newp = MEM_mapallocN(memh->len, "dupli_mapalloc");
277                 else
278                         newp = MEM_mallocN(memh->len, "dupli_alloc");
279
280                 if (newp == NULL) return NULL;
281 #else
282                 {
283                         MemHead *nmemh;
284                         char *name = malloc(strlen(memh->name) + 24);
285
286                         if (memh->mmap) {
287                                 sprintf(name, "%s %s", "dupli_mapalloc", memh->name);
288                                 newp = MEM_mapallocN(memh->len, name);
289                         }
290                         else {
291                                 sprintf(name, "%s %s", "dupli_alloc", memh->name);
292                                 newp = MEM_mallocN(memh->len, name);
293                         }
294
295                         if (newp == NULL) return NULL;
296
297                         nmemh = newp;
298                         nmemh--;
299
300                         nmemh->need_free_name = 1;
301                 }
302 #endif
303
304                 memcpy(newp, vmemh, memh->len);
305         }
306
307         return newp;
308 }
309
310 void *MEM_reallocN(void *vmemh, size_t len)
311 {
312         void *newp = NULL;
313         
314         if (vmemh) {
315                 MemHead *memh = vmemh;
316                 memh--;
317
318                 newp = MEM_mallocN(len, memh->name);
319                 if (newp) {
320                         if (len < memh->len) {
321                                 /* shrink */
322                                 memcpy(newp, vmemh, len);
323                         }
324                         else {
325                                 /* grow (or remain same size) */
326                                 memcpy(newp, vmemh, memh->len);
327                         }
328                 }
329
330                 MEM_freeN(vmemh);
331         }
332
333         return newp;
334 }
335
336 void *MEM_recallocN(void *vmemh, size_t len)
337 {
338         void *newp = NULL;
339
340         if (vmemh) {
341                 MemHead *memh = vmemh;
342                 memh--;
343
344                 newp = MEM_mallocN(len, memh->name);
345                 if (newp) {
346                         if (len < memh->len) {
347                                 /* shrink */
348                                 memcpy(newp, vmemh, len);
349                         }
350                         else {
351                                 memcpy(newp, vmemh, memh->len);
352
353                                 if (len > memh->len) {
354                                         /* grow */
355                                         /* zero new bytes */
356                                         memset(((char *)newp) + memh->len, 0, len - memh->len);
357                                 }
358                         }
359                 }
360
361                 MEM_freeN(vmemh);
362         }
363
364         return newp;
365 }
366
367 static void make_memhead_header(MemHead *memh, size_t len, const char *str)
368 {
369         MemTail *memt;
370         
371         memh->tag1 = MEMTAG1;
372         memh->name = str;
373         memh->nextname = NULL;
374         memh->len = len;
375         memh->mmap = 0;
376         memh->tag2 = MEMTAG2;
377
378 #ifdef DEBUG_MEMDUPLINAME
379         memh->need_free_name = 0;
380 #endif
381         
382         memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
383         memt->tag3 = MEMTAG3;
384         
385         addtail(membase, &memh->next);
386         if (memh->next) {
387                 memh->nextname = MEMNEXT(memh->next)->name;
388         }
389         
390         totblock++;
391         mem_in_use += len;
392
393         peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
394 }
395
396 void *MEM_mallocN(size_t len, const char *str)
397 {
398         MemHead *memh;
399
400         mem_lock_thread();
401
402         len = (len + 3) & ~3;   /* allocate in units of 4 */
403         
404         memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
405
406         if (memh) {
407                 make_memhead_header(memh, len, str);
408                 mem_unlock_thread();
409                 if (malloc_debug_memset && len)
410                         memset(memh + 1, 255, len);
411
412 #ifdef DEBUG_MEMCOUNTER
413                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
414                         memcount_raise(__func__);
415                 memh->_count = _mallocn_count++;
416 #endif
417                 return (++memh);
418         }
419         mem_unlock_thread();
420         print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
421                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
422         return NULL;
423 }
424
425 void *MEM_callocN(size_t len, const char *str)
426 {
427         MemHead *memh;
428
429         mem_lock_thread();
430
431         len = (len + 3) & ~3;   /* allocate in units of 4 */
432
433         memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
434
435         if (memh) {
436                 make_memhead_header(memh, len, str);
437                 mem_unlock_thread();
438 #ifdef DEBUG_MEMCOUNTER
439                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
440                         memcount_raise(__func__);
441                 memh->_count = _mallocn_count++;
442 #endif
443                 return (++memh);
444         }
445         mem_unlock_thread();
446         print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
447                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
448         return NULL;
449 }
450
451 /* note; mmap returns zero'd memory */
452 void *MEM_mapallocN(size_t len, const char *str)
453 {
454         MemHead *memh;
455
456         mem_lock_thread();
457         
458         len = (len + 3) & ~3;   /* allocate in units of 4 */
459
460         memh = mmap(NULL, len + sizeof(MemHead) + sizeof(MemTail),
461                     PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
462
463         if (memh != (MemHead *)-1) {
464                 make_memhead_header(memh, len, str);
465                 memh->mmap = 1;
466                 mmap_in_use += len;
467                 peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
468                 mem_unlock_thread();
469 #ifdef DEBUG_MEMCOUNTER
470                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
471                         memcount_raise(__func__);
472                 memh->_count = _mallocn_count++;
473 #endif
474                 return (++memh);
475         }
476         else {
477                 mem_unlock_thread();
478                 print_error("Mapalloc returns null, fallback to regular malloc: "
479                             "len=" SIZET_FORMAT " in %s, total %u\n",
480                             SIZET_ARG(len), str, (unsigned int) mmap_in_use);
481                 return MEM_callocN(len, str);
482         }
483 }
484
485 /* Memory statistics print */
486 typedef struct MemPrintBlock {
487         const char *name;
488         uintptr_t len;
489         int items;
490 } MemPrintBlock;
491
492 static int compare_name(const void *p1, const void *p2)
493 {
494         const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
495         const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
496
497         return strcmp(pb1->name, pb2->name);
498 }
499
500 static int compare_len(const void *p1, const void *p2)
501 {
502         const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
503         const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
504
505         if (pb1->len < pb2->len)
506                 return 1;
507         else if (pb1->len == pb2->len)
508                 return 0;
509         else
510                 return -1;
511 }
512
513 void MEM_printmemlist_stats(void)
514 {
515         MemHead *membl;
516         MemPrintBlock *pb, *printblock;
517         int totpb, a, b;
518
519         mem_lock_thread();
520
521         /* put memory blocks into array */
522         printblock = malloc(sizeof(MemPrintBlock) * totblock);
523
524         pb = printblock;
525         totpb = 0;
526
527         membl = membase->first;
528         if (membl) membl = MEMNEXT(membl);
529
530         while (membl) {
531                 pb->name = membl->name;
532                 pb->len = membl->len;
533                 pb->items = 1;
534
535                 totpb++;
536                 pb++;
537
538                 if (membl->next)
539                         membl = MEMNEXT(membl->next);
540                 else break;
541         }
542
543         /* sort by name and add together blocks with the same name */
544         qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
545         for (a = 0, b = 0; a < totpb; a++) {
546                 if (a == b) {
547                         continue;
548                 }
549                 else if (strcmp(printblock[a].name, printblock[b].name) == 0) {
550                         printblock[b].len += printblock[a].len;
551                         printblock[b].items++;
552                 }
553                 else {
554                         b++;
555                         memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
556                 }
557         }
558         totpb = b + 1;
559
560         /* sort by length and print */
561         qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
562         printf("\ntotal memory len: %.3f MB\n",
563                (double)mem_in_use / (double)(1024 * 1024));
564         printf("peak memory len: %.3f MB\n",
565                (double)peak_mem / (double)(1024 * 1024));
566         printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
567         for (a = 0, pb = printblock; a < totpb; a++, pb++) {
568                 printf("%6d (%8.3f  %8.3f) %s\n",
569                        pb->items, (double)pb->len / (double)(1024 * 1024),
570                        (double)pb->len / 1024.0 / (double)pb->items, pb->name);
571         }
572         free(printblock);
573         
574         mem_unlock_thread();
575
576 #if 0 /* GLIBC only */
577         malloc_stats();
578 #endif
579 }
580
581 static const char mem_printmemlist_pydict_script[] =
582 "mb_userinfo = {}\n"
583 "totmem = 0\n"
584 "for mb_item in membase:\n"
585 "    mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
586 "    mb_item_user_size[0] += 1 # Add a user\n"
587 "    mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
588 "    totmem += mb_item['len']\n"
589 "print('(membase) items:', len(membase), '| unique-names:',\n"
590 "      len(mb_userinfo), '| total-mem:', totmem)\n"
591 "mb_userinfo_sort = list(mb_userinfo.items())\n"
592 "for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
593 "                             ('users', lambda a: -a[1][0]),\n"
594 "                             ('name', lambda a: a[0])):\n"
595 "    print('\\nSorting by:', sort_name)\n"
596 "    mb_userinfo_sort.sort(key = sort_func)\n"
597 "    for item in mb_userinfo_sort:\n"
598 "        print('name:%%s, users:%%i, len:%%i' %%\n"
599 "              (item[0], item[1][0], item[1][1]))\n";
600
601 /* Prints in python syntax for easy */
602 static void MEM_printmemlist_internal(int pydict)
603 {
604         MemHead *membl;
605
606         mem_lock_thread();
607
608         membl = membase->first;
609         if (membl) membl = MEMNEXT(membl);
610         
611         if (pydict) {
612                 print_error("# membase_debug.py\n");
613                 print_error("membase = [\n");
614         }
615         while (membl) {
616                 if (pydict) {
617                         fprintf(stderr,
618                                 "    {'len':" SIZET_FORMAT ", "
619                                 "'name':'''%s''', "
620                                 "'pointer':'%p'},\n",
621                                 SIZET_ARG(membl->len), membl->name, (void *)(membl + 1));
622                 }
623                 else {
624 #ifdef DEBUG_MEMCOUNTER
625                         print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
626                                     membl->name, SIZET_ARG(membl->len), membl + 1,
627                                     membl->_count);
628 #else
629                         print_error("%s len: " SIZET_FORMAT " %p\n",
630                                     membl->name, SIZET_ARG(membl->len), membl + 1);
631 #endif
632                 }
633                 if (membl->next)
634                         membl = MEMNEXT(membl->next);
635                 else break;
636         }
637         if (pydict) {
638                 fprintf(stderr, "]\n\n");
639                 fprintf(stderr, mem_printmemlist_pydict_script);
640         }
641         
642         mem_unlock_thread();
643 }
644
645 void MEM_callbackmemlist(void (*func)(void *))
646 {
647         MemHead *membl;
648
649         mem_lock_thread();
650
651         membl = membase->first;
652         if (membl) membl = MEMNEXT(membl);
653
654         while (membl) {
655                 func(membl + 1);
656                 if (membl->next)
657                         membl = MEMNEXT(membl->next);
658                 else break;
659         }
660
661         mem_unlock_thread();
662 }
663
664 short MEM_testN(void *vmemh)
665 {
666         MemHead *membl;
667
668         mem_lock_thread();
669
670         membl = membase->first;
671         if (membl) membl = MEMNEXT(membl);
672
673         while (membl) {
674                 if (vmemh == membl + 1) {
675                         mem_unlock_thread();
676                         return 1;
677                 }
678
679                 if (membl->next)
680                         membl = MEMNEXT(membl->next);
681                 else break;
682         }
683
684         mem_unlock_thread();
685
686         print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
687         return 0;
688 }
689
690 void MEM_printmemlist(void)
691 {
692         MEM_printmemlist_internal(0);
693 }
694 void MEM_printmemlist_pydict(void)
695 {
696         MEM_printmemlist_internal(1);
697 }
698
699 short MEM_freeN(void *vmemh)
700 {
701         short error = 0;
702         MemTail *memt;
703         MemHead *memh = vmemh;
704         const char *name;
705
706         if (memh == NULL) {
707                 MemorY_ErroR("free", "attempt to free NULL pointer");
708                 /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
709                 return(-1);
710         }
711
712         if (sizeof(intptr_t) == 8) {
713                 if (((intptr_t) memh) & 0x7) {
714                         MemorY_ErroR("free", "attempt to free illegal pointer");
715                         return(-1);
716                 }
717         }
718         else {
719                 if (((intptr_t) memh) & 0x3) {
720                         MemorY_ErroR("free", "attempt to free illegal pointer");
721                         return(-1);
722                 }
723         }
724         
725         memh--;
726         if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
727                 MemorY_ErroR(memh->name, "double free");
728                 return(-1);
729         }
730
731         mem_lock_thread();
732         if ((memh->tag1 == MEMTAG1) &&
733             (memh->tag2 == MEMTAG2) &&
734             ((memh->len & 0x3) == 0))
735         {
736                 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
737                 if (memt->tag3 == MEMTAG3) {
738                         
739                         memh->tag1 = MEMFREE;
740                         memh->tag2 = MEMFREE;
741                         memt->tag3 = MEMFREE;
742                         /* after tags !!! */
743                         rem_memblock(memh);
744
745                         mem_unlock_thread();
746                         
747                         return(0);
748                 }
749                 error = 2;
750                 MemorY_ErroR(memh->name, "end corrupt");
751                 name = check_memlist(memh);
752                 if (name != NULL) {
753                         if (name != memh->name) MemorY_ErroR(name, "is also corrupt");
754                 }
755         }
756         else {
757                 error = -1;
758                 name = check_memlist(memh);
759                 if (name == NULL)
760                         MemorY_ErroR("free", "pointer not in memlist");
761                 else
762                         MemorY_ErroR(name, "error in header");
763         }
764
765         totblock--;
766         /* here a DUMP should happen */
767
768         mem_unlock_thread();
769
770         return(error);
771 }
772
773 /* --------------------------------------------------------------------- */
774 /* local functions                                                       */
775 /* --------------------------------------------------------------------- */
776
777 static void addtail(volatile localListBase *listbase, void *vlink)
778 {
779         struct localLink *link = vlink;
780
781         /* for a generic API error checks here is fine but
782          * the limited use here they will never be NULL */
783 #if 0
784         if (link == NULL) return;
785         if (listbase == NULL) return;
786 #endif
787
788         link->next = NULL;
789         link->prev = listbase->last;
790
791         if (listbase->last) ((struct localLink *)listbase->last)->next = link;
792         if (listbase->first == NULL) listbase->first = link;
793         listbase->last = link;
794 }
795
796 static void remlink(volatile localListBase *listbase, void *vlink)
797 {
798         struct localLink *link = vlink;
799
800         /* for a generic API error checks here is fine but
801          * the limited use here they will never be NULL */
802 #if 0
803         if (link == NULL) return;
804         if (listbase == NULL) return;
805 #endif
806
807         if (link->next) link->next->prev = link->prev;
808         if (link->prev) link->prev->next = link->next;
809
810         if (listbase->last == link) listbase->last = link->prev;
811         if (listbase->first == link) listbase->first = link->next;
812 }
813
814 static void rem_memblock(MemHead *memh)
815 {
816         remlink(membase, &memh->next);
817         if (memh->prev) {
818                 if (memh->next)
819                         MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
820                 else
821                         MEMNEXT(memh->prev)->nextname = NULL;
822         }
823
824         totblock--;
825         mem_in_use -= memh->len;
826
827 #ifdef DEBUG_MEMDUPLINAME
828         if (memh->need_free_name)
829                 free((char *) memh->name);
830 #endif
831
832         if (memh->mmap) {
833                 mmap_in_use -= memh->len;
834                 if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
835                         printf("Couldn't unmap memory %s\n", memh->name);
836         }
837         else {
838                 if (malloc_debug_memset && memh->len)
839                         memset(memh + 1, 255, memh->len);
840                 free(memh);
841         }
842 }
843
844 static void MemorY_ErroR(const char *block, const char *error)
845 {
846         print_error("Memoryblock %s: %s\n", block, error);
847
848 #ifdef WITH_ASSERT_ABORT
849         abort();
850 #endif
851 }
852
853 static const char *check_memlist(MemHead *memh)
854 {
855         MemHead *forw, *back, *forwok, *backok;
856         const char *name;
857
858         forw = membase->first;
859         if (forw) forw = MEMNEXT(forw);
860         forwok = NULL;
861         while (forw) {
862                 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
863                 forwok = forw;
864                 if (forw->next) forw = MEMNEXT(forw->next);
865                 else forw = NULL;
866         }
867
868         back = (MemHead *) membase->last;
869         if (back) back = MEMNEXT(back);
870         backok = NULL;
871         while (back) {
872                 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
873                 backok = back;
874                 if (back->prev) back = MEMNEXT(back->prev);
875                 else back = NULL;
876         }
877
878         if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
879
880         if (forw == NULL && back == NULL) {
881                 /* no wrong headers found then but in search of memblock */
882
883                 forw = membase->first;
884                 if (forw) forw = MEMNEXT(forw);
885                 forwok = NULL;
886                 while (forw) {
887                         if (forw == memh) break;
888                         if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
889                         forwok = forw;
890                         if (forw->next) forw = MEMNEXT(forw->next);
891                         else forw = NULL;
892                 }
893                 if (forw == NULL) return NULL;
894
895                 back = (MemHead *) membase->last;
896                 if (back) back = MEMNEXT(back);
897                 backok = NULL;
898                 while (back) {
899                         if (back == memh) break;
900                         if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
901                         backok = back;
902                         if (back->prev) back = MEMNEXT(back->prev);
903                         else back = NULL;
904                 }
905         }
906
907         if (forwok) name = forwok->nextname;
908         else name = "No name found";
909
910         if (forw == memh) {
911                 /* to be sure but this block is removed from the list */
912                 if (forwok) {
913                         if (backok) {
914                                 forwok->next = (MemHead *)&backok->next;
915                                 backok->prev = (MemHead *)&forwok->next;
916                                 forwok->nextname = backok->name;
917                         }
918                         else {
919                                 forwok->next = NULL;
920                                 membase->last = (struct localLink *) &forwok->next;
921                         }
922                 }
923                 else {
924                         if (backok) {
925                                 backok->prev = NULL;
926                                 membase->first = &backok->next;
927                         }
928                         else {
929                                 membase->first = membase->last = NULL;
930                         }
931                 }
932         }
933         else {
934                 MemorY_ErroR(name, "Additional error in header");
935                 return("Additional error in header");
936         }
937
938         return(name);
939 }
940
941 uintptr_t MEM_get_peak_memory(void)
942 {
943         uintptr_t _peak_mem;
944
945         mem_lock_thread();
946         _peak_mem = peak_mem;
947         mem_unlock_thread();
948
949         return _peak_mem;
950 }
951
952 void MEM_reset_peak_memory(void)
953 {
954         mem_lock_thread();
955         peak_mem = 0;
956         mem_unlock_thread();
957 }
958
959 uintptr_t MEM_get_memory_in_use(void)
960 {
961         uintptr_t _mem_in_use;
962
963         mem_lock_thread();
964         _mem_in_use = mem_in_use;
965         mem_unlock_thread();
966
967         return _mem_in_use;
968 }
969
970 uintptr_t MEM_get_mapped_memory_in_use(void)
971 {
972         uintptr_t _mmap_in_use;
973
974         mem_lock_thread();
975         _mmap_in_use = mmap_in_use;
976         mem_unlock_thread();
977
978         return _mmap_in_use;
979 }
980
981 int MEM_get_memory_blocks_in_use(void)
982 {
983         int _totblock;
984
985         mem_lock_thread();
986         _totblock = totblock;
987         mem_unlock_thread();
988
989         return _totblock;
990 }
991
992 #ifndef NDEBUG
993 const char *MEM_name_ptr(void *vmemh)
994 {
995         if (vmemh) {
996                 MemHead *memh = vmemh;
997                 memh--;
998                 return memh->name;
999         }
1000         else {
1001                 return "MEM_name_ptr(NULL)";
1002         }
1003 }
1004 #endif  /* NDEBUG */