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