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